<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Vinayak G Hejib</title>
    <description>The latest articles on DEV Community by Vinayak G Hejib (@vnayak_hejib).</description>
    <link>https://dev.to/vnayak_hejib</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3287815%2Fc98117cb-7e0c-4c88-81cc-988f590f6dee.png</url>
      <title>DEV Community: Vinayak G Hejib</title>
      <link>https://dev.to/vnayak_hejib</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/vnayak_hejib"/>
    <language>en</language>
    <item>
      <title># Unstructured Concurrency in iOS</title>
      <dc:creator>Vinayak G Hejib</dc:creator>
      <pubDate>Thu, 12 Mar 2026 17:13:29 +0000</pubDate>
      <link>https://dev.to/vnayak_hejib/-unstructured-concurrency-in-ios-1pl7</link>
      <guid>https://dev.to/vnayak_hejib/-unstructured-concurrency-in-ios-1pl7</guid>
      <description>&lt;p&gt;A few months ago, I started noticing something strange in my app.&lt;/p&gt;

&lt;p&gt;Nothing was crashing.&lt;br&gt;
Nothing obvious was broken.&lt;/p&gt;

&lt;p&gt;But occasionally:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  users saw stale data appear briefly &lt;/li&gt;
&lt;li&gt;  network requests were triggered twice &lt;/li&gt;
&lt;li&gt;  UI updates happened after a screen had already disappeared&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Naturally, we checked the usual suspects:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  race conditions &lt;/li&gt;
&lt;li&gt;  caching bugs &lt;/li&gt;
&lt;li&gt;  API inconsistencies&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Everything looked fine.&lt;/p&gt;

&lt;p&gt;Until we started tracing the execution paths.&lt;/p&gt;

&lt;p&gt;That's when we found them.&lt;/p&gt;

&lt;p&gt;Not one. &lt;br&gt;
Not two.&lt;/p&gt;

&lt;p&gt;But &lt;strong&gt;dozens of tiny &lt;code&gt;Task {}&lt;/code&gt; blocks scattered across the codebase&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Each one looked harmless during code review. &lt;br&gt;
Each one worked perfectly in isolation.&lt;/p&gt;

&lt;p&gt;But together they created something far more subtle:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;unstructured concurrency&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;And once we looked deeper, we discovered something even more&lt;br&gt;
interesting.&lt;/p&gt;

&lt;p&gt;Many of those tasks were also interacting with &lt;strong&gt;&lt;code&gt;@MainActor&lt;/code&gt;-isolated&lt;br&gt;
code&lt;/strong&gt; in ways that quietly turned the UI thread into a traffic&lt;br&gt;
bottleneck.&lt;/p&gt;

&lt;p&gt;No crashes.&lt;br&gt;
No compiler warnings.&lt;/p&gt;

&lt;p&gt;Just &lt;strong&gt;strange behavior that only appeared under real user&lt;br&gt;
interaction&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The kind of bugs that make you stare at logs for hours.&lt;/p&gt;

&lt;p&gt;This article is about those bugs.&lt;/p&gt;

&lt;p&gt;And more importantly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  why &lt;strong&gt;unstructured concurrency&lt;/strong&gt; often causes them &lt;/li&gt;
&lt;li&gt;  how &lt;strong&gt;&lt;code&gt;@MainActor&lt;/code&gt; can accidentally amplify them&lt;/strong&gt; &lt;/li&gt;
&lt;li&gt;  and how a few simple patterns can make Swift concurrency behave the
way it was actually designed to.&lt;/li&gt;
&lt;/ul&gt;


&lt;h1&gt;
  
  
  The Promise of Structured Concurrency
&lt;/h1&gt;

&lt;p&gt;Swift's concurrency model is built around a simple but powerful idea:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Tasks should have a clear parent--child relationship.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;When a parent task is cancelled, its children cancel automatically.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;loadProduct&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;throws&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Product&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;details&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetchDetails&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;reviews&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetchReviews&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="kt"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nv"&gt;details&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;details&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nv"&gt;reviews&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;reviews&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  both child tasks belong to &lt;code&gt;loadProduct&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;  if the parent cancels → children cancel&lt;/li&gt;
&lt;li&gt;  errors propagate cleanly&lt;/li&gt;
&lt;li&gt;  lifetimes are predictable&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is &lt;strong&gt;structured concurrency&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Think of it like a well-managed engineering team: everyone knows who&lt;br&gt;
they report to.&lt;/p&gt;


&lt;h1&gt;
  
  
  Then Someone Writes This
&lt;/h1&gt;

&lt;p&gt;Sooner or later, someone writes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kt"&gt;Task&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetchProducts&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Looks harmless. &lt;br&gt;
Feels convenient.&lt;/p&gt;

&lt;p&gt;But this is &lt;strong&gt;unstructured concurrency&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That task:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  has &lt;strong&gt;no parent&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;  has &lt;strong&gt;no ownership&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;  has &lt;strong&gt;no automatic cancellation&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It's basically a &lt;strong&gt;free‑roaming asynchronous creature&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Once created, it just... keeps living its life.&lt;/p&gt;


&lt;h1&gt;
  
  
  The Ghost Task Problem
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2mu33bt73t0t00ms4qwh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2mu33bt73t0t00ms4qwh.png" alt=" " width="800" height="446"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's look at a SwiftUI example.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;struct&lt;/span&gt; &lt;span class="kt"&gt;ProductView&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;View&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;some&lt;/span&gt; &lt;span class="kt"&gt;View&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Products"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;onAppear&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="kt"&gt;Task&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;viewModel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loadProducts&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;User opens the screen → task starts.&lt;/p&gt;

&lt;p&gt;User navigates away immediately → view disappears.&lt;/p&gt;

&lt;p&gt;But the task?&lt;/p&gt;

&lt;p&gt;Still running.&lt;/p&gt;

&lt;p&gt;Because that &lt;code&gt;Task {}&lt;/code&gt; has &lt;strong&gt;no relationship with the view lifecycle&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Congratulations.&lt;/p&gt;

&lt;p&gt;You just created a &lt;strong&gt;ghost task&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Eventually it might finish and update state for a view that &lt;strong&gt;no longer&lt;br&gt;
exists&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;These bugs are subtle, unpredictable, and extremely hard to trace.&lt;/p&gt;


&lt;h1&gt;
  
  
  The Accidental Parallelism Problem
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flysxhlsrywm7j416wurd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flysxhlsrywm7j416wurd.png" alt=" " width="800" height="446"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Unstructured tasks also create hidden duplication.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;refresh&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;Task&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;repository&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loadData&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;User taps refresh twice quickly.&lt;/p&gt;

&lt;p&gt;Now you have &lt;strong&gt;two independent tasks&lt;/strong&gt; fetching the same data.&lt;/p&gt;

&lt;p&gt;Possible outcomes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  duplicated requests&lt;/li&gt;
&lt;li&gt;  racing state updates&lt;/li&gt;
&lt;li&gt;  overwritten results&lt;/li&gt;
&lt;li&gt;  inflated analytics metrics&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And during code review, nobody notices because:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"It's just a &lt;code&gt;Task {}&lt;/code&gt;."&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h1&gt;
  
  
  The Uncancelled Work Problem
&lt;/h1&gt;

&lt;p&gt;A very common example appears in view models.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="kt"&gt;ProductViewModel&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;ObservableObject&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;loadProducts&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;Task&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;products&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetchProducts&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;products&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;products&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Looks perfectly normal.&lt;/p&gt;

&lt;p&gt;But the view model &lt;strong&gt;cannot cancel this task&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  the screen disappears &lt;/li&gt;
&lt;li&gt;  the user logs out &lt;/li&gt;
&lt;li&gt;  the view model is recreated&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;...the task keeps running anyway.&lt;/p&gt;

&lt;p&gt;You've effectively lost control over your concurrency.&lt;/p&gt;




&lt;h1&gt;
  
  
  The Structured Alternative
&lt;/h1&gt;

&lt;p&gt;SwiftUI actually gives us a much better option.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;task&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;viewModel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loadProducts&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;.task&lt;/code&gt; modifier automatically ties the task to the &lt;strong&gt;view&lt;br&gt;
lifecycle&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;When the view disappears:&lt;/p&gt;

&lt;p&gt;the task is &lt;strong&gt;cancelled automatically&lt;/strong&gt;.&lt;/p&gt;


&lt;h1&gt;
  
  
  Now Enter &lt;code&gt;@MainActor&lt;/code&gt;
&lt;/h1&gt;
&lt;h3&gt;
  
  
  And Why &lt;code&gt;@MainActor&lt;/code&gt; Is Both a Blessing... and a Trap
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu15k3222i2jqfg1xfu1u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu15k3222i2jqfg1xfu1u.png" alt=" " width="800" height="446"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If &lt;code&gt;Task {}&lt;/code&gt; is the most casually abused API in Swift concurrency, &lt;br&gt;
&lt;code&gt;@MainActor&lt;/code&gt; might be a close second.&lt;/p&gt;

&lt;p&gt;Before Swift concurrency, UI updates looked like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kt"&gt;DispatchQueue&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;titleLabel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Swift improved this with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;@MainActor&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="kt"&gt;ProductViewModel&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;ObservableObject&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;@Published&lt;/span&gt; &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;products&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now UI updates are automatically safe.&lt;/p&gt;

&lt;p&gt;Clean. &lt;br&gt;
Elegant. &lt;br&gt;
Predictable.&lt;/p&gt;

&lt;p&gt;But like many convenient tools in software engineering...&lt;/p&gt;

&lt;p&gt;it's also easy to misuse.&lt;/p&gt;


&lt;h1&gt;
  
  
  The "Just Add @MainActor" Fix
&lt;/h1&gt;

&lt;p&gt;Soon someone sees a concurrency warning and writes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;@MainActor&lt;/span&gt;
&lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;loadProducts&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;products&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetchProducts&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;products&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;products&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Looks harmless.&lt;/p&gt;

&lt;p&gt;But now the &lt;strong&gt;entire function executes on the main actor&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Which means orchestration begins on the UI executor.&lt;/p&gt;

&lt;p&gt;Multiply this across several view models and suddenly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  UI work &lt;/li&gt;
&lt;li&gt;  async orchestration &lt;/li&gt;
&lt;li&gt;  state mutation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;...all compete on the same actor.&lt;/p&gt;

&lt;p&gt;Your main thread becomes a &lt;strong&gt;traffic intersection at rush hour&lt;/strong&gt;.&lt;/p&gt;




&lt;h1&gt;
  
  
  The MainActor ViewModel Trap
&lt;/h1&gt;

&lt;p&gt;Another common pattern:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;@MainActor&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="kt"&gt;FeedViewModel&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;ObservableObject&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;loadFeed&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;posts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetchFeed&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;posts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;posts&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now &lt;strong&gt;every method in the class runs on the main actor&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Including things that shouldn't:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  JSON parsing&lt;/li&gt;
&lt;li&gt;  sorting large datasets&lt;/li&gt;
&lt;li&gt;  heavy data transformations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The UI thread quietly becomes your &lt;strong&gt;data processing engine&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Scrolling performance will eventually protest.&lt;/p&gt;




&lt;h1&gt;
  
  
  The Better Mental Model
&lt;/h1&gt;

&lt;p&gt;Think of &lt;code&gt;@MainActor&lt;/code&gt; as &lt;strong&gt;UI state protection&lt;/strong&gt;, not a concurrency&lt;br&gt;
shortcut.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="kt"&gt;FeedViewModel&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;ObservableObject&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="kd"&gt;@MainActor&lt;/span&gt;
    &lt;span class="kd"&gt;@Published&lt;/span&gt; &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

    &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;loadFeed&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;posts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetchFeed&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="kt"&gt;MainActor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;posts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;posts&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  networking happens off the main actor&lt;/li&gt;
&lt;li&gt;  computation happens off the main actor&lt;/li&gt;
&lt;li&gt;  only UI state touches the main actor&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Exactly what we want.&lt;/p&gt;




&lt;h1&gt;
  
  
  When Both Problems Meet
&lt;/h1&gt;

&lt;p&gt;The most interesting bugs appear when &lt;strong&gt;unstructured tasks meet careless&lt;br&gt;
&lt;code&gt;@MainActor&lt;/code&gt; usage&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kt"&gt;Task&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;viewModel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loadFeed&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If &lt;code&gt;loadFeed()&lt;/code&gt; is &lt;code&gt;@MainActor&lt;/code&gt;, you now have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  an &lt;strong&gt;unstructured task&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;  executing on the &lt;strong&gt;main actor&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;  doing work that might not belong there&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Perfect recipe for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  UI hiccups&lt;/li&gt;
&lt;li&gt;  mysterious delays&lt;/li&gt;
&lt;li&gt;  hard‑to‑reproduce bugs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The kind that only appear &lt;strong&gt;in production&lt;/strong&gt;.&lt;/p&gt;




&lt;h1&gt;
  
  
  A Simple Rule I Follow
&lt;/h1&gt;

&lt;p&gt;Whenever I see:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kt"&gt;Task&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Who owns this task?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And whenever I see:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;@MainActor&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="kt"&gt;Something&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Is this truly UI state, or just convenient isolation?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Those two questions alone catch &lt;strong&gt;a surprising number of concurrency&lt;br&gt;
bugs&lt;/strong&gt;.&lt;/p&gt;




&lt;h1&gt;
  
  
  Final Thoughts
&lt;/h1&gt;

&lt;p&gt;Structured concurrency gives us:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  predictable task lifetimes&lt;/li&gt;
&lt;li&gt;  automatic cancellation&lt;/li&gt;
&lt;li&gt;  safe error propagation&lt;/li&gt;
&lt;li&gt;  clear ownership&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Unstructured concurrency gives us:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  invisible background work&lt;/li&gt;
&lt;li&gt;  duplicated tasks&lt;/li&gt;
&lt;li&gt;  uncancellable operations&lt;/li&gt;
&lt;li&gt;  unpredictable behavior&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And careless &lt;code&gt;@MainActor&lt;/code&gt; usage can quietly turn the &lt;strong&gt;UI thread into a&lt;br&gt;
bottleneck&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Or as I like to think about it:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Structured concurrency is like a well‑run orchestra. &lt;br&gt;
Unstructured concurrency is everyone playing instruments whenever they&lt;br&gt;
feel like it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Sometimes it still sounds okay.&lt;/p&gt;

&lt;p&gt;Until it suddenly doesn't.&lt;/p&gt;

</description>
      <category>codequality</category>
      <category>ios</category>
      <category>mobile</category>
      <category>swift</category>
    </item>
    <item>
      <title>Memory Leaks Are Architecture Problems</title>
      <dc:creator>Vinayak G Hejib</dc:creator>
      <pubDate>Mon, 05 Jan 2026 09:09:45 +0000</pubDate>
      <link>https://dev.to/vnayak_hejib/memory-leaks-are-architecture-problems-emh</link>
      <guid>https://dev.to/vnayak_hejib/memory-leaks-are-architecture-problems-emh</guid>
      <description>&lt;p&gt;&lt;strong&gt;Audience:&lt;/strong&gt; Majority iOS Engineers  &lt;/p&gt;

&lt;p&gt;&lt;em&gt;Written for engineers who debug systems not syntax.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Context:&lt;/strong&gt; Large, modular, long-lived production apps&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fliqbcz196ibsr2m1wyg5.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fliqbcz196ibsr2m1wyg5.jpeg" alt=" " width="800" height="429"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The Comfortable Lie We All Learned
&lt;/h2&gt;

&lt;p&gt;(Why &lt;code&gt;[weak self]&lt;/code&gt; Is Not the Fix You Think It Is (UIKit + SwiftUI))&lt;/p&gt;

&lt;p&gt;For years, most of us, iOS developers have been told:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“If you have a memory leak, just add &lt;code&gt;[weak self]&lt;/code&gt;.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It sounds reasonable.&lt;br&gt;&lt;br&gt;
It sounds technical.&lt;br&gt;&lt;br&gt;
And it’s fundamentally incomplete.&lt;/p&gt;

&lt;p&gt;In real-world iOS applications especially those with multiple teams, modules, coordinators, and long-lived services, &lt;strong&gt;memory leaks are rarely caused by missing &lt;code&gt;weak&lt;/code&gt; keywords&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;They are caused by &lt;strong&gt;architecture&lt;/strong&gt;.&lt;/p&gt;


&lt;h2&gt;
  
  
  The Core Truth
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Memory leaks are not language problems.&lt;br&gt;&lt;br&gt;
They are ownership problems.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Swift is memory-safe. ARC works.&lt;br&gt;
What fails is our understanding of &lt;strong&gt;who owns what and for how long&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Once an app grows beyond a few screens, memory management stops being about syntax and becomes a &lt;strong&gt;systems design concern&lt;/strong&gt;.&lt;/p&gt;


&lt;h2&gt;
  
  
  The &lt;code&gt;[weak self]&lt;/code&gt; Myth
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy5bc8suhly9jw0smxkfn.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy5bc8suhly9jw0smxkfn.jpeg" alt=" " width="800" height="559"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Most teams follow this pattern:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;App starts leaking&lt;/li&gt;
&lt;li&gt;Instruments shows retained objects&lt;/li&gt;
&lt;li&gt;Someone adds &lt;code&gt;[weak self]&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Leak “goes away”&lt;/li&gt;
&lt;li&gt;Six weeks (or may be 2-3 sprints) later, it’s back!!!&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Why?&lt;/p&gt;

&lt;p&gt;Because &lt;code&gt;[weak self]&lt;/code&gt; does &lt;strong&gt;not&lt;/strong&gt; define ownership.&lt;br&gt;&lt;br&gt;
It only avoids &lt;strong&gt;one specific retain cycle&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;[weak self]&lt;/code&gt; hides the symptom, it does not fix the cause.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If your architecture is wrong, the leak will resurface elsewhere.&lt;/p&gt;


&lt;h2&gt;
  
  
  Real-World Leak #1: ViewModels Without a Lifetime / ViewModel as a God Object
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh0cepfr2iuy3fjnr1kbe.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh0cepfr2iuy3fjnr1kbe.jpeg" alt=" " width="800" height="597"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The most common production leak.&lt;/p&gt;
&lt;h3&gt;
  
  
  How it starts
&lt;/h3&gt;

&lt;p&gt;A ViewModel is created to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Hold state&lt;/li&gt;
&lt;li&gt;Handle user actions&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  How it grows
&lt;/h3&gt;

&lt;p&gt;Soon it owns:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Network calls&lt;/li&gt;
&lt;li&gt;Navigation&lt;/li&gt;
&lt;li&gt;Analytics&lt;/li&gt;
&lt;li&gt;Feature flags&lt;/li&gt;
&lt;li&gt;Deep links&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Eventually you get:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ViewController → ViewModel → Services → Closures → ViewController
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Root cause:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
The ViewModel has &lt;strong&gt;no clearly defined lifetime&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Should it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Should it die with the screen?&lt;/li&gt;
&lt;li&gt;Should it live across navigation?&lt;/li&gt;
&lt;li&gt;Be shared across flows?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When the answer is unclear, ARC cannot help you.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Learning:  When an object owns “everything”, nothing can release it safely.&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h2&gt;
  
  
  Real-World Leak #2: Long-Lived Objects Owning Short-Lived Ones
&lt;/h2&gt;

&lt;p&gt;This is subtle and extremely destructive.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft9hwy6ddu9b8u1p7ba6e.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft9hwy6ddu9b8u1p7ba6e.jpeg" alt=" " width="800" height="446"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Common examples
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;AppCoordinator&lt;/code&gt; retaining screen callbacks&lt;/li&gt;
&lt;li&gt;Singleton managers holding ViewModels&lt;/li&gt;
&lt;li&gt;Global caches storing closures/callbacks
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kt"&gt;AnalyticsManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shared&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;track&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;viewModel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;handleResult&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Even if &lt;code&gt;viewModel&lt;/code&gt; is weak, the &lt;strong&gt;manager’s lifetime outlives the screen&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Rule:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
A long-lived object must never own a short-lived one.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If it does, leaks are guaranteed.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Learning:  A long-lived object must never own a short-lived one, directly or indirectly.&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h2&gt;
  
  
  Combine: When Ownership Is Ignored
&lt;/h2&gt;

&lt;p&gt;I would say, Combine didn’t introduce leaks.&lt;br&gt;
It exposed architectural mistakes(or may be it just amplified bad architecture)!!!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwyqi9ddqs5glgyrtn8vk.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwyqi9ddqs5glgyrtn8vk.jpeg" alt=" " width="800" height="594"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For example, the culprit&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="n"&gt;publisher&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sink&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;
        &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;updateUI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;store&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;in&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;cancellables&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The leak depends on &lt;strong&gt;where &lt;code&gt;cancellables&lt;/code&gt; lives&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ViewController → usually safe
&lt;/li&gt;
&lt;li&gt;ViewModel → depends on lifetime
&lt;/li&gt;
&lt;li&gt;Singleton / Coordinator → leak&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Learning: Combine pipelines must obey the same lifetime rules as views or they leak quietly.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  &lt;code&gt;NotificationCenter&lt;/code&gt; Is Not the Villain
&lt;/h2&gt;

&lt;p&gt;NotificationCenter leaks are usually &lt;strong&gt;symptoms&lt;/strong&gt;, not causes.&lt;/p&gt;

&lt;p&gt;If &lt;code&gt;deinit&lt;/code&gt; isn’t called, ask:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Who is retaining this object?&lt;/li&gt;
&lt;li&gt;Why does it live longer than expected?&lt;/li&gt;
&lt;li&gt;⚠️ &lt;code&gt;NotificationCenter&lt;/code&gt; is rarely the root cause, it’s the first visible symptom of bad ownership.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Timers &amp;amp; DisplayLinks: Perfect Leak Multipliers
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kt"&gt;Timer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;scheduledTimer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;withTimeInterval&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;repeats&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;
    &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;refresh&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Even with &lt;code&gt;[weak self]&lt;/code&gt;, this leaks if:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The timer isn’t invalidated&lt;/li&gt;
&lt;li&gt;The run loop outlives the screen&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Learning: Repeating resources must be explicitly tied to lifecycle; architecture decides that, not syntax.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h1&gt;
  
  
  SwiftUI: Memory Leaks Hidden Behind Simplicity
&lt;/h1&gt;

&lt;p&gt;SwiftUI didn’t remove memory management.&lt;br&gt;&lt;br&gt;
It &lt;strong&gt;made it implicit&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That makes ownership mistakes easier and harder to detect.&lt;/p&gt;




&lt;h2&gt;
  
  
  SwiftUI Leak Pattern #1: &lt;code&gt;@StateObject&lt;/code&gt; in the Wrong Place
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;struct&lt;/span&gt; &lt;span class="kt"&gt;ScreenView&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;View&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;@StateObject&lt;/span&gt; &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;viewModel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;ScreenViewModel&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If &lt;code&gt;ScreenView&lt;/code&gt; is recreated:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The ViewModel may survive longer than expected&lt;/li&gt;
&lt;li&gt;Or be recreated repeatedly&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;If you don’t control &lt;strong&gt;where&lt;/strong&gt; an object is created,&lt;br&gt;
you don’t control &lt;strong&gt;when&lt;/strong&gt; it dies.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;@StateObject doesn’t manage memory, it declares ownership.&lt;br&gt;
And ownership must match lifecycle.&lt;/p&gt;




&lt;h2&gt;
  
  
  SwiftUI Leak Pattern #2: EnvironmentObjects With No Exit
&lt;/h2&gt;

&lt;p&gt;EnvironmentObjects are effectively &lt;strong&gt;global ownership&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If they:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Hold services&lt;/li&gt;
&lt;li&gt;Retain closures&lt;/li&gt;
&lt;li&gt;Reference UI state&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;They will live far longer than the UI expects.&lt;/p&gt;

&lt;p&gt;Environment ≠ lifecycle.&lt;/p&gt;




&lt;h2&gt;
  
  
  SwiftUI Leak Pattern #3: View Identity Mistakes
&lt;/h2&gt;

&lt;p&gt;Incorrect &lt;code&gt;id&lt;/code&gt; usage causes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;View recreation&lt;/li&gt;
&lt;li&gt;Object graph duplication&lt;/li&gt;
&lt;li&gt;State retention bugs
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kt"&gt;List&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;\&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;
    &lt;span class="kt"&gt;RowView&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;item&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If identity is unstable, memory behavior is unpredictable.&lt;/p&gt;




&lt;h2&gt;
  
  
  SwiftUI Leak Pattern #4: Navigation Retains State
&lt;/h2&gt;

&lt;p&gt;Navigation stacks can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Retain views&lt;/li&gt;
&lt;li&gt;Retain ViewModels&lt;/li&gt;
&lt;li&gt;Retain closures&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Even when UI disappears.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Navigation is ownership.&lt;br&gt;
Treat it as such.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Architecture Smells That Predict Leaks
&lt;/h2&gt;

&lt;p&gt;If you see these, leaks are inevitable:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ViewModels doing navigation&lt;/li&gt;
&lt;li&gt;Singletons coordinating flows&lt;/li&gt;
&lt;li&gt;Managers with no lifecycle&lt;/li&gt;
&lt;li&gt;Extensions as dumping grounds&lt;/li&gt;
&lt;li&gt;“Convenience” APIs growing endlessly&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are not style issues.&lt;br&gt;
They are &lt;strong&gt;runtime memory failures waiting to happen&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  How "Good" Engineers Debug Leaks
&lt;/h2&gt;

&lt;p&gt;Not by guessing!!!&lt;/p&gt;

&lt;h3&gt;
  
  
  Actual workflow
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Instruments → Allocations&lt;/li&gt;
&lt;li&gt;Memory Graph Debugger&lt;/li&gt;
&lt;li&gt;Identify retention chains&lt;/li&gt;
&lt;li&gt;Ask one question:&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Should this object still be alive?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If the answer is no, the architecture is wrong.&lt;/p&gt;

&lt;p&gt;You don’t hunt leaks.&lt;br&gt;
You &lt;strong&gt;prove ownership is invalid&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Architectural Fix(Not a tiplist)
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcozyx4npl10omci45pmd.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcozyx4npl10omci45pmd.jpeg" alt=" " width="800" height="458"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Strong apps have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Explicit lifetimes&lt;/li&gt;
&lt;li&gt;Directional ownership&lt;/li&gt;
&lt;li&gt;Short-lived objects owned by short-lived parents&lt;/li&gt;
&lt;li&gt;Long-lived objects that never retain UI&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This applies equally to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;UIKit&lt;/li&gt;
&lt;li&gt;SwiftUI&lt;/li&gt;
&lt;li&gt;Combine&lt;/li&gt;
&lt;li&gt;Coordinators&lt;/li&gt;
&lt;li&gt;Dependency graphs&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Final Truth
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Memory leaks are not Swift problems.&lt;br&gt;&lt;br&gt;
They are architectural lies revealed by runtime.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Fix ownership.&lt;br&gt;&lt;br&gt;
Define lifetimes.&lt;br&gt;&lt;br&gt;
Leaks disappear naturally.&lt;/p&gt;




</description>
      <category>ios</category>
      <category>swift</category>
      <category>mobile</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Memory Leaks in SwiftUI(Real-World Examples)</title>
      <dc:creator>Vinayak G Hejib</dc:creator>
      <pubDate>Thu, 20 Nov 2025 09:46:27 +0000</pubDate>
      <link>https://dev.to/vnayak_hejib/memory-leaks-in-swiftui-where-they-hide-how-to-catch-them-real-world-examples-84i</link>
      <guid>https://dev.to/vnayak_hejib/memory-leaks-in-swiftui-where-they-hide-how-to-catch-them-real-world-examples-84i</guid>
      <description>&lt;p&gt;Memory Leaks in SwiftUI - Where They Hide &amp;amp; How to Catch Them!!&lt;/p&gt;

&lt;p&gt;SwiftUI makes UI feel effortles, some SwiftUI views exit the screen. but others cling to RAM like it’s the last samosa 😉&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6q4n2z9ldq75ezvkvqje.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6q4n2z9ldq75ezvkvqje.jpeg" alt=" " width="800" height="427"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Have you ever observed something highly interactive customization flow—started behaving oddly? Nothing crashes, Nothing obviously lags, but each time we navigate through the flow, the app feels &lt;em&gt;heavier&lt;/em&gt;. Not slow… just “sticky,” like the UI was dragging unseen baggage behind it.&lt;/p&gt;

&lt;p&gt;I has a similar situation and after 10–15 navigations, Instruments confirmed my suspicion:&lt;br&gt;&lt;br&gt;
&lt;strong&gt;A whole family of views, view models, timers, and async tasks were still alive.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Not one or two.&lt;br&gt;&lt;br&gt;
All of them.&lt;/p&gt;

&lt;p&gt;It wasn’t a Combine pipeline gone rogue.&lt;br&gt;&lt;br&gt;
It wasn’t AVPlayer.&lt;br&gt;&lt;br&gt;
It wasn’t some UIKit weirdness.&lt;/p&gt;

&lt;p&gt;It was a &lt;strong&gt;single misused &lt;code&gt;@StateObject&lt;/code&gt; buried inside a child view&lt;/strong&gt;.&lt;/p&gt;


&lt;h2&gt;
  
  
  The Real Leak That Triggered the Investigation
&lt;/h2&gt;

&lt;p&gt;Inside the main customization screen:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;@StateObject&lt;/span&gt; &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;viewModel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;BigViewModel&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Perfect.&lt;/p&gt;

&lt;p&gt;But deep within a child view:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;@StateObject&lt;/span&gt; &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;viewModel&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;BigViewModel&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;instead of:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;@ObservedObject&lt;/span&gt; &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;viewModel&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;BigViewModel&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That tiny slip created a &lt;strong&gt;permanent retention cycle&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Every navigation pushed a new &lt;code&gt;BigViewModel&lt;/code&gt; into memory.&lt;br&gt;&lt;br&gt;
None ever left.&lt;br&gt;
The leak was silent until the user repeated the flow enough times for the app to feel heavier.&lt;/p&gt;

&lt;p&gt;Real world.  Hard to catch.  Easy to create.&lt;/p&gt;


&lt;h2&gt;
  
  
  Where SwiftUI Actually Hides Leaks (Real Examples from Real Apps)
&lt;/h2&gt;
&lt;h3&gt;
  
  
  &lt;strong&gt;1. Wrong property wrapper (&lt;code&gt;@StateObject&lt;/code&gt; vs &lt;code&gt;@ObservedObject&lt;/code&gt;)&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgxz98o6oiy8h308f45hu.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgxz98o6oiy8h308f45hu.jpeg" alt=" " width="800" height="749"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is the #1 cause I’ve seen in large SwiftUI codebases.&lt;/p&gt;

&lt;p&gt;SwiftUI assumes &lt;code&gt;@StateObject&lt;/code&gt; owns the lifecycle.&lt;br&gt;&lt;br&gt;
Child views often live longer than expected (transitions, animations, NavigationStack caching).&lt;/p&gt;
&lt;h2&gt;
  
  
  One wrong wrapper → immortal view model.
&lt;/h2&gt;
&lt;h3&gt;
  
  
  &lt;strong&gt;2. Async Tasks that silently retain &lt;code&gt;self&lt;/code&gt;&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl3g2m64hpy0bccjbstdb.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl3g2m64hpy0bccjbstdb.jpeg" alt=" " width="800" height="759"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I ran into a bug where a discount recalculation fired on every color change:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;onChange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;of&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;selectedColor&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;
    &lt;span class="kt"&gt;Task&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;viewModel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;recomputeDiscount&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Harmless-looking, but &lt;code&gt;Task&lt;/code&gt; &lt;strong&gt;captures strongly by default&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The recalculation job kept the entire view model—and its whole dependency graph—alive.&lt;/p&gt;

&lt;p&gt;The fix:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kt"&gt;Task&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;weak&lt;/span&gt; &lt;span class="n"&gt;viewModel&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;viewModel&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;recomputeDiscount&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  &lt;strong&gt;3. Timers that were never cancelled&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;A carousel featured an auto-scroll timer:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsuhea6hmxz2h2p6mex10.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsuhea6hmxz2h2p6mex10.jpeg" alt=" " width="800" height="772"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kt"&gt;Timer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;publish&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;every&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;on&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;in&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;common&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;autoconnect&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sink&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nobody cancelled it.&lt;/p&gt;

&lt;p&gt;Every “dead” carousel screen stayed… well… very much alive.&lt;/p&gt;

&lt;h2&gt;
  
  
  You don’t notice until you have 20 of them.
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;4. NavigationPath captured in a ViewModel&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;This one surprised even senior devs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="kt"&gt;NavVM&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;NavigationPath&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The minute you capture the entire &lt;code&gt;NavigationPath&lt;/code&gt;, SwiftUI may never free the views, because the path retains every element in it.&lt;/p&gt;

&lt;h2&gt;
  
  
  If you must observe navigation, observe tokens / IDs, never the full path object.
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;5. EnvironmentObjects retaining everything&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Shared state is great—until it becomes a black hole.&lt;/p&gt;

&lt;p&gt;A global theme object held references to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;feature view models
&lt;/li&gt;
&lt;li&gt;child store objects
&lt;/li&gt;
&lt;li&gt;even closures that referenced views indirectly
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;EnvironmentObjects should be &lt;strong&gt;pure app state&lt;/strong&gt;, not dependency containers.&lt;/p&gt;




&lt;h2&gt;
  
  
  How I Catch Leaks Today (and Sleep Better)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;1. Add &lt;code&gt;deinit&lt;/code&gt; logs to every ViewModel&lt;/strong&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;deinit&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"deinit: BigViewModel"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you don’t see this when popping a screen → it’s leaking.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;2. Make &lt;code&gt;[weak self]&lt;/code&gt; a reflex&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Tasks, closures, timers, Combine pipelines — treat them all as suspects.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;3. Audit property wrapper changes during code reviews&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Catch wrong wrappers early.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;4. Stress-test navigation&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Push/pull a flow 20–30 times.&lt;br&gt;&lt;br&gt;
Leaks reveal themselves through repetition.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Thought
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu8296kggog2n4u5b6bm4.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu8296kggog2n4u5b6bm4.jpeg" alt=" " width="800" height="425"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;SwiftUI’s power comes from its declarative nature.&lt;br&gt;&lt;br&gt;
But the lifecycle behind it is not always intuitive—and sometimes, a single misplaced wrapper or async closure can keep entire view trees alive.&lt;/p&gt;

&lt;p&gt;Once you know where leaks hide, you’ll start seeing them every week.&lt;/p&gt;

&lt;p&gt;And trust me:&lt;br&gt;&lt;br&gt;
Crushing a stubborn leak feels ridiculously satisfying.&lt;/p&gt;

</description>
      <category>ios</category>
      <category>swift</category>
      <category>performance</category>
      <category>swiftui</category>
    </item>
    <item>
      <title>The Essentials of Unit Testing in iOS - A Quick Guide</title>
      <dc:creator>Vinayak G Hejib</dc:creator>
      <pubDate>Tue, 09 Sep 2025 15:19:26 +0000</pubDate>
      <link>https://dev.to/vnayak_hejib/the-essentials-of-unit-testing-in-ios-a-quick-guide-3031</link>
      <guid>https://dev.to/vnayak_hejib/the-essentials-of-unit-testing-in-ios-a-quick-guide-3031</guid>
      <description>&lt;p&gt;Unit testing is the &lt;strong&gt;backbone of robust, maintainable iOS apps&lt;/strong&gt;. Think of it like having a safety net under a tightrope, it won’t stop you from writing risky code, but it will catch you if things go wrong. Writing reliable tests ensures your code behaves as expected, prevents regressions, and gives developers confidence when refactoring (without that dreaded “did I just break production?” moment).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fojey6u6j1avn7wke8l3l.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fojey6u6j1avn7wke8l3l.jpeg" alt=" " width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this post, we’ll take a &lt;strong&gt;practical (and slightly fun) tour of unit testing in iOS&lt;/strong&gt;. We’ll cover:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;TDD (Test-Driven Development)&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dependency Injection&lt;/strong&gt; for testability
&lt;/li&gt;
&lt;li&gt;The world of &lt;strong&gt;Test Doubles&lt;/strong&gt; : Dummy, Stub, Fake, Mock, Spy (yes, they sound like a crime thriller cast)
&lt;/li&gt;
&lt;li&gt;Testing strategies for &lt;strong&gt;legacy codebases&lt;/strong&gt; with missing test coverage
&lt;/li&gt;
&lt;li&gt;Practical &lt;strong&gt;testing standards and best practices&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And to make it concrete, we’ll walk through everything with a &lt;strong&gt;Login flow example&lt;/strong&gt;, because if there’s one thing every app has, it’s a login screen (and if it fails, your users won’t stick around).&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Why Unit Testing Matters in iOS
&lt;/h2&gt;

&lt;p&gt;Unit tests verify individual components of your app in isolation. In iOS, this could be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;strong&gt;ViewModel&lt;/strong&gt; in SwiftUI or UIKit (MVVM)
&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;network layer&lt;/strong&gt; that authenticates users
&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;utility&lt;/strong&gt; or business logic class
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Benefits:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Detect regressions early
&lt;/li&gt;
&lt;li&gt;Make refactoring safer
&lt;/li&gt;
&lt;li&gt;Enable Test-Driven Development (TDD)
&lt;/li&gt;
&lt;li&gt;Document expected behavior
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By testing individual units, you ensure your app behaves as expected even as the codebase grows or changes.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Test-Driven Development (TDD)
&lt;/h2&gt;

&lt;p&gt;TDD is a disciplined approach to building software:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Write a &lt;strong&gt;failing test&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Write minimal code to make it &lt;strong&gt;pass&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Refactor&lt;/strong&gt; for readability and efficiency
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8x2b9yh6ydwwad604jaz.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8x2b9yh6ydwwad604jaz.jpeg" alt=" " width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;LoginViewModel Example:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;struct&lt;/span&gt; &lt;span class="kt"&gt;User&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;protocol&lt;/span&gt; &lt;span class="kt"&gt;AuthServiceProtocol&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;login&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;User&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="kt"&gt;LoginViewModel&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;authService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;AuthServiceProtocol&lt;/span&gt;
    &lt;span class="kd"&gt;private(set)&lt;/span&gt; &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;loggedInUser&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;User&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;

    &lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;authService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;AuthServiceProtocol&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;authService&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;authService&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;login&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;loggedInUser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;authService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;login&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This setup allows TDD to guide your design while keeping the code testable and maintainable.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Test Doubles: Dummy, Stub, Fake, Mock, Spy
&lt;/h2&gt;

&lt;p&gt;Test doubles allow you to &lt;strong&gt;isolate the unit under test&lt;/strong&gt; from dependencies. Each type has a specific role:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpvi0pkdheuyet3hdhivc.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpvi0pkdheuyet3hdhivc.jpeg" alt=" " width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;th&gt;Example in iOS&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Dummy&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Passed but never used&lt;/td&gt;
&lt;td&gt;Placeholder AuthService to satisfy method signature&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Stub&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Returns fixed responses&lt;/td&gt;
&lt;td&gt;AuthServiceStub returning predetermined login results&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Fake&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Lightweight working implementation&lt;/td&gt;
&lt;td&gt;In-memory login service for testing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Mock&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Records method calls, allows verification&lt;/td&gt;
&lt;td&gt;Verify that login() is called exactly once&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Spy&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Records calls and behavior&lt;/td&gt;
&lt;td&gt;Ensure delegate methods or callbacks are triggered&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Examples with Login Flow
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Dummy&lt;/strong&gt; – used just to satisfy dependency injection, not actually used by the test.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="kt"&gt;AuthServiceDummy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;AuthServiceProtocol&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;login&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;User&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;nil&lt;/span&gt; &lt;span class="c1"&gt;// Not used in this test&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c1"&gt;// Example: Passing this to LoginViewModel ensures the constructor compiles, &lt;/span&gt;
&lt;span class="c1"&gt;// even though we won’t call login().&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Stub&lt;/strong&gt; – provides predefined outputs so tests can run deterministically.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="kt"&gt;AuthServiceStub&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;AuthServiceProtocol&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;login&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;User&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"test"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"1234"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kt"&gt;User&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"abc123"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;nil&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c1"&gt;// Example: Ensures LoginViewModel always gets a known response for given inputs.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Fake&lt;/strong&gt; – a simplified but working implementation (e.g., in-memory user store).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="kt"&gt;AuthServiceFake&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;AuthServiceProtocol&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;users&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"test"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"1234"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;login&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;User&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;guard&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;storedPassword&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;storedPassword&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kt"&gt;User&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"fakeToken123"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c1"&gt;// Example: Acts like a real auth service without network calls.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Mock&lt;/strong&gt; – verifies that specific methods are invoked.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="kt"&gt;AuthServiceMock&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;AuthServiceProtocol&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;loginCalled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;

    &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;login&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;User&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;loginCalled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;nil&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c1"&gt;// Example: Lets us check if LoginViewModel actually triggered login().&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Spy&lt;/strong&gt; – tracks how many times or with what data a method is called.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="kt"&gt;AuthServiceSpy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;AuthServiceProtocol&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private(set)&lt;/span&gt; &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;loginCallCount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

    &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;login&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;User&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;loginCallCount&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kt"&gt;User&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"spyToken"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c1"&gt;// Example: Useful for verifying multiple login attempts are handled.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  4. Dependency Injection (DI)
&lt;/h2&gt;

&lt;p&gt;Hard-coded dependencies make testing difficult. &lt;strong&gt;DI&lt;/strong&gt; allows injecting stubs, fakes, or mocks:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F481r6d3sz3ygxo66fare.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F481r6d3sz3ygxo66fare.jpeg" alt=" " width="800" height="800"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;stubService&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;AuthServiceStub&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;viewModel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;LoginViewModel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;authService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;stubService&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This makes your tests &lt;strong&gt;deterministic and independent&lt;/strong&gt; of network or database services.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. Legacy Code and Incremental Testing
&lt;/h2&gt;

&lt;p&gt;Testing legacy apps requires careful planning:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Identify &lt;strong&gt;critical flows&lt;/strong&gt; (e.g., login success/failure)
&lt;/li&gt;
&lt;li&gt;Introduce &lt;strong&gt;protocols and DI&lt;/strong&gt; to decouple code
&lt;/li&gt;
&lt;li&gt;Write &lt;strong&gt;unit tests incrementally&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Use &lt;strong&gt;test doubles&lt;/strong&gt; to isolate untestable parts
&lt;/li&gt;
&lt;li&gt;Focus on &lt;strong&gt;core logic&lt;/strong&gt; over 100% coverage initially
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Refactoring Example:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;protocol&lt;/span&gt; &lt;span class="kt"&gt;AuthFetcherProtocol&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;login&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;User&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="kt"&gt;LegacyLoginManager&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;fetcher&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;AuthFetcherProtocol&lt;/span&gt;

    &lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;fetcher&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;AuthFetcherProtocol&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fetcher&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fetcher&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;login&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;User&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;fetcher&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;login&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="kt"&gt;AuthFetcherStub&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;AuthFetcherProtocol&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;login&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;User&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kt"&gt;User&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"legacyToken"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Test:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;testLegacyManagerLogin&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;stub&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;AuthFetcherStub&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;manager&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;LegacyLoginManager&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;fetcher&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;stub&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="kt"&gt;XCTAssertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;manager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;login&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"any"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"any"&lt;/span&gt;&lt;span class="p"&gt;)?&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"legacyToken"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  6. Testing Standards and Best Practices
&lt;/h2&gt;

&lt;p&gt;For effective unit testing in iOS:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;AAA Pattern (Arrange-Act-Assert):&lt;/strong&gt; Structure tests clearly.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keep tests independent:&lt;/strong&gt; No shared state between tests.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Name tests descriptively:&lt;/strong&gt; &lt;code&gt;testLoginSucceedsWithValidCredentials&lt;/code&gt; &amp;gt; &lt;code&gt;testLogin1&lt;/code&gt;.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test behavior, not implementation:&lt;/strong&gt; Verify outcomes, not internal details.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use coverage as a guide, not a goal:&lt;/strong&gt; Focus on meaningful tests.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fast feedback:&lt;/strong&gt; Unit tests should run in seconds.
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  7. Efficient Testing Strategies
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Start with small units:&lt;/strong&gt; ViewModels, services, utilities
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use DI and test doubles&lt;/strong&gt; to avoid network/db dependencies
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Separate functional vs. unit tests:&lt;/strong&gt; Logic vs. UI interactions
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Leverage XCTest features:&lt;/strong&gt; &lt;code&gt;XCTAssert&lt;/code&gt;, &lt;code&gt;XCTestExpectation&lt;/code&gt;, &lt;code&gt;measure&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Incrementally cover legacy code:&lt;/strong&gt; Test core flows first
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Unit testing ensures &lt;strong&gt;maintainable, bug-free apps&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;TDD, DI, and test doubles make testing &lt;strong&gt;fast and isolated&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Use &lt;strong&gt;Dummy, Stub, Fake, Mock, and Spy&lt;/strong&gt; for different testing scenarios
&lt;/li&gt;
&lt;li&gt;Legacy code can be incrementally tested and refactored
&lt;/li&gt;
&lt;li&gt;Follow &lt;strong&gt;standards and best practices&lt;/strong&gt; for clarity and reliability
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ios</category>
      <category>testing</category>
      <category>swift</category>
    </item>
    <item>
      <title>LLMs in Xcode: How AI Will Change iOS Development Forever</title>
      <dc:creator>Vinayak G Hejib</dc:creator>
      <pubDate>Mon, 01 Sep 2025 09:54:40 +0000</pubDate>
      <link>https://dev.to/vnayak_hejib/llms-in-xcode-how-ai-will-change-ios-development-forever-1kgk</link>
      <guid>https://dev.to/vnayak_hejib/llms-in-xcode-how-ai-will-change-ios-development-forever-1kgk</guid>
      <description>&lt;h2&gt;
  
  
  🎯 Artificial Intelligence (AI) isn’t just knocking on the door of iOS development....it’s already in the room!!!
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8qj946uk666tpl0q0dl8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8qj946uk666tpl0q0dl8.png" alt=" " width="800" height="1200"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With Large Language Models (LLMs) becoming more deeply integrated into developer tools, Xcode itself is on the brink of an AI-powered revolution. The shift won’t just make coding faster—it will fundamentally transform how iOS apps are built, tested, and shipped.&lt;/p&gt;

&lt;p&gt;Let’s dive into how LLMs will reshape iOS development, why this change is inevitable, and what it means for developers like us.&lt;/p&gt;




&lt;h2&gt;
  
  
  💻 From Autocomplete to "Auto-Engineer"
&lt;/h2&gt;

&lt;p&gt;For years, Xcode has had autocomplete and code snippets. Useful? Yes. But LLMs take this to another level. Instead of just suggesting the next token, AI can &lt;strong&gt;understand your intent&lt;/strong&gt;. Imagine:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Writing &lt;code&gt;func fetchUsers&lt;/code&gt; and AI scaffolds the entire &lt;code&gt;URLSession&lt;/code&gt; code with error handling and decoding.&lt;/li&gt;
&lt;li&gt;Building a SwiftUI view where AI not only completes your layout but suggests accessibility improvements or performance tweaks.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is no longer autocomplete, it’s &lt;strong&gt;auto-engineering&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Architecture as Code, Not Afterthought
&lt;/h2&gt;

&lt;p&gt;Most teams struggle with consistency across modules. With LLMs, architecture decisions won’t live only in design docs—they’ll &lt;strong&gt;manifest directly in code&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Want MVVM with Combine in one feature and Redux-style in another? AI can enforce architectural boundaries automatically.&lt;/li&gt;
&lt;li&gt;Boilerplate ViewModels, Coordinators, and Dependency Injection setups will no longer consume engineering hours.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This allows developers to spend less time reinventing the wheel and more time focusing on &lt;strong&gt;business logic and user experience&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  AI-Powered Testing: From Fragile to Smart
&lt;/h2&gt;

&lt;p&gt;Unit testing is often treated like a chore. But what if Xcode’s AI assistant could:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Generate unit tests&lt;/strong&gt; automatically for every new function.&lt;/li&gt;
&lt;li&gt;Suggest edge cases you forgot about (like offline mode, invalid tokens, or failed JSON decoding).&lt;/li&gt;
&lt;li&gt;Highlight flaky tests and rewrite them for stability.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Even better, LLMs could analyze &lt;strong&gt;test coverage gaps&lt;/strong&gt; and propose new tests—something most teams don’t have the time to do rigorously.&lt;/p&gt;




&lt;h2&gt;
  
  
  Debugging with Context, Not Guesswork
&lt;/h2&gt;

&lt;p&gt;Debugging is often about piecing together logs and stack traces. LLMs can flip this process:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You paste a crash log, and AI explains in plain English why it happened.&lt;/li&gt;
&lt;li&gt;Instead of just stopping at the line of code, AI correlates it with recent PRs, API contract changes, or even dependency upgrades.&lt;/li&gt;
&lt;li&gt;AI could even &lt;strong&gt;simulate fixes&lt;/strong&gt; and let you preview the impact before you change anything.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That’s not just debugging, that’s &lt;strong&gt;proactive problem solving&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Security and Performance as Defaults
&lt;/h2&gt;

&lt;p&gt;Performance and security are usually afterthoughts until a bottleneck or breach appears. With AI in Xcode:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AI can flag &lt;strong&gt;potential security risks&lt;/strong&gt; like storing tokens in &lt;code&gt;UserDefaults&lt;/code&gt; or weak SSL handling.&lt;/li&gt;
&lt;li&gt;It can recommend &lt;strong&gt;performance optimizations&lt;/strong&gt;—like caching strategies, async handling improvements, or reducing unnecessary SwiftUI view recompositions.&lt;/li&gt;
&lt;li&gt;AI could act as a &lt;strong&gt;real-time reviewer&lt;/strong&gt;, catching issues before they ever make it to PR.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Collaboration Reimagined
&lt;/h2&gt;

&lt;p&gt;Xcode projects often have a steep onboarding curve. Imagine a new developer joining your team:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Instead of reading endless Confluence pages, they can &lt;strong&gt;chat with Xcode’s AI assistant&lt;/strong&gt;:
&lt;em&gt;“How does networking work in this project?”&lt;/em&gt;
&lt;em&gt;“Where are payments handled?”&lt;/em&gt;
&lt;em&gt;“What does this feature flag control?”&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The AI doesn’t just search files—it &lt;strong&gt;understands project architecture&lt;/strong&gt;, speeding up onboarding and reducing reliance on tribal knowledge.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Developer’s New Role
&lt;/h2&gt;

&lt;p&gt;Some fear AI will replace developers. But in reality, AI will act more like a &lt;strong&gt;supercharged pair programmer&lt;/strong&gt;. The developer’s role will evolve from typing code to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Defining intent&lt;/strong&gt;: what the app should achieve, not how every line looks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reviewing AI output&lt;/strong&gt;: ensuring correctness, architecture alignment, and edge-case handling.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Elevating creativity&lt;/strong&gt;: focusing on user experience, design, and innovation instead of boilerplate.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Instead of replacing us, LLMs will amplify us.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Road Ahead
&lt;/h2&gt;

&lt;p&gt;It’s not science fiction—GitHub Copilot is already integrated with Xcode, and Apple is quietly investing in its own AI tooling. In the next few years, expect:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Built-in &lt;strong&gt;AI code generation in Xcode&lt;/strong&gt; (beyond Copilot).&lt;/li&gt;
&lt;li&gt;AI-assisted &lt;strong&gt;SwiftUI previews&lt;/strong&gt; that optimize performance automatically.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test-first workflows&lt;/strong&gt; where AI generates tests before you even write the implementation.&lt;/li&gt;
&lt;li&gt;AI &lt;strong&gt;refactor engines&lt;/strong&gt; that can migrate legacy Objective-C code to Swift or convert Combine-based code to async/await seamlessly.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Prompt Engineering for iOS Developers: Speaking the AI’s Language
&lt;/h2&gt;

&lt;p&gt;While Xcode may soon integrate AI into our workflows, the real productivity boost depends on how well we communicate with LLMs. That’s where &lt;strong&gt;prompt engineering&lt;/strong&gt; comes in.&lt;/p&gt;

&lt;h3&gt;
  
  
  Be Specific, Not Vague
&lt;/h3&gt;

&lt;p&gt;Instead of asking:  &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Fix this bug”  &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Try:  &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Fix the retain cycle in this SwiftUI View that uses @StateObject incorrectly.”  &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The more context you give, the better the response.&lt;/p&gt;




&lt;h3&gt;
  
  
  Show, Don’t Tell
&lt;/h3&gt;

&lt;p&gt;Always paste the relevant &lt;strong&gt;code snippet&lt;/strong&gt; rather than describing it.&lt;br&gt;&lt;br&gt;
LLMs respond far better to &lt;strong&gt;actual code&lt;/strong&gt; than vague summaries.&lt;/p&gt;




&lt;h3&gt;
  
  
  Set the Role
&lt;/h3&gt;

&lt;p&gt;Start prompts with roles like:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;“You are an expert iOS architect”
&lt;/li&gt;
&lt;li&gt;“You are an Apple-style API designer”
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This frames the answer in the &lt;strong&gt;style you want&lt;/strong&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  Chain of Thought with Guidance
&lt;/h3&gt;

&lt;p&gt;Break a request into steps:  &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“First explain the bug, then suggest fixes, then show corrected code.”  &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This leads to more &lt;strong&gt;structured, usable responses&lt;/strong&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  Iterative Refinement
&lt;/h3&gt;

&lt;p&gt;Don’t settle for the first reply. Instead, ask the AI to:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;“Optimize further”
&lt;/li&gt;
&lt;li&gt;“Make it Swifty”
&lt;/li&gt;
&lt;li&gt;“Reduce complexity”
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Iteration sharpens the results.&lt;/p&gt;




&lt;h3&gt;
  
  
  Combine AI + Human Review
&lt;/h3&gt;

&lt;p&gt;AI suggestions should &lt;strong&gt;never be merged blindly&lt;/strong&gt; into production code.&lt;br&gt;&lt;br&gt;
Treat AI as a &lt;strong&gt;pair programmer&lt;/strong&gt; — helpful, fast, but still in need of human review.&lt;/p&gt;




&lt;h2&gt;
  
  
  💡 Final Thoughts
&lt;/h2&gt;

&lt;p&gt;The rise of LLMs in Xcode will make iOS development faster, smarter, and more consistent. But the real winners won’t be developers who just “use AI”—they’ll be developers who &lt;strong&gt;guide AI&lt;/strong&gt;. The ones who can blend architectural thinking, product vision, and creativity with AI’s raw coding power.&lt;/p&gt;

&lt;p&gt;The future isn’t about developers vs AI.&lt;br&gt;&lt;br&gt;
It’s about developers &lt;strong&gt;with AI&lt;/strong&gt; and together, we’ll build apps at a scale and quality that was once impossible.&lt;/p&gt;




</description>
      <category>ios</category>
      <category>githubcopilot</category>
      <category>swift</category>
      <category>mobile</category>
    </item>
    <item>
      <title>The Secret Life of @State in SwiftUI: Where Does My Data Actually Live?</title>
      <dc:creator>Vinayak G Hejib</dc:creator>
      <pubDate>Wed, 13 Aug 2025 10:39:31 +0000</pubDate>
      <link>https://dev.to/vnayak_hejib/the-secret-life-of-state-in-swiftui-where-does-my-data-actually-live-3be9</link>
      <guid>https://dev.to/vnayak_hejib/the-secret-life-of-state-in-swiftui-where-does-my-data-actually-live-3be9</guid>
      <description>&lt;p&gt;&lt;em&gt;Understanding the truth behind SwiftUI’s most misunderstood property wrapper.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;Why This Matters&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Every SwiftUI developer uses &lt;code&gt;@State&lt;/code&gt;. It’s the “magic” that makes your UI update when data changes.  &lt;/p&gt;

&lt;p&gt;But here’s the problem:&lt;br&gt;&lt;br&gt;
Most devs don’t actually know &lt;strong&gt;where that data is stored&lt;/strong&gt;, &lt;strong&gt;how it survives view reloads&lt;/strong&gt;, or &lt;strong&gt;why it sometimes behaves unexpectedly&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Misunderstanding &lt;code&gt;@State&lt;/code&gt; leads to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;❌ Views not updating
&lt;/li&gt;
&lt;li&gt;❌ Data unexpectedly resetting
&lt;/li&gt;
&lt;li&gt;❌ Performance hits from unnecessary re-renders
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s lift the hood.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnpwuzv5vdlctnizj00jf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnpwuzv5vdlctnizj00jf.png" alt=" " width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  &lt;strong&gt;The Basics — What You Think Happens&lt;/strong&gt;
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;struct&lt;/span&gt; &lt;span class="kt"&gt;CounterView&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;View&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;@State&lt;/span&gt; &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;some&lt;/span&gt; &lt;span class="kt"&gt;View&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;VStack&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kt"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\(&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="kt"&gt;Button&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Increment"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;It &lt;em&gt;feels&lt;/em&gt; like:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;count&lt;/code&gt; is just a normal property on the &lt;code&gt;CounterView&lt;/code&gt; struct.
&lt;/li&gt;
&lt;li&gt;When &lt;code&gt;count&lt;/code&gt; changes, SwiftUI magically re-renders the view.
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;But… that’s &lt;strong&gt;not&lt;/strong&gt; the whole story.&lt;/p&gt;
&lt;h2&gt;
  
  
  &lt;strong&gt;1. First Misconception — “@State is just a stored property”&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Nope. It’s &lt;strong&gt;not&lt;/strong&gt; stored inside your View struct.  &lt;/p&gt;

&lt;p&gt;SwiftUI views are value types — they get recreated all the time.&lt;br&gt;&lt;br&gt;
If &lt;code&gt;@State&lt;/code&gt; lived directly in your struct, you’d lose its value every time your view re-rendered.  &lt;/p&gt;

&lt;p&gt;Instead, &lt;code&gt;@State&lt;/code&gt; is a property wrapper that stores your data &lt;strong&gt;outside&lt;/strong&gt; the view, in a special SwiftUI-managed heap storage.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Think of it like this:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your View struct is the blueprint.
&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;@State&lt;/code&gt; storage is the hidden container SwiftUI manages.
&lt;/li&gt;
&lt;li&gt;The two are connected by an &lt;strong&gt;ID&lt;/strong&gt; SwiftUI assigns when the view hierarchy is built.&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  &lt;strong&gt;2. What Really Happens When You Declare &lt;code&gt;@State&lt;/code&gt;&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;When SwiftUI first creates your view:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;It allocates a hidden storage object (a “State box”).&lt;/li&gt;
&lt;li&gt;This object lives for as long as the view identity stays the same in the hierarchy.&lt;/li&gt;
&lt;li&gt;Your view gets a reference to that box through the &lt;code&gt;wrappedValue&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Key point:&lt;/strong&gt; View identity.&lt;br&gt;&lt;br&gt;
Change the identity (like swapping one &lt;code&gt;.id&lt;/code&gt; for another in a &lt;code&gt;List&lt;/code&gt;), and the storage disappears — &lt;code&gt;@State&lt;/code&gt; resets to its initial value.&lt;/p&gt;


&lt;h2&gt;
  
  
  &lt;strong&gt;3. The Lifecycle in Real Time&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Let’s track it:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Initialization&lt;/strong&gt; → SwiftUI allocates storage outside the struct.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mutation&lt;/strong&gt; → You update the value, SwiftUI schedules a re-render.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Recreation&lt;/strong&gt; → Your struct is replaced, but the &lt;code&gt;@State&lt;/code&gt; box is reattached.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Destruction&lt;/strong&gt; → Identity changes → storage is destroyed.
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;💡 &lt;em&gt;Real-world pitfall:&lt;/em&gt; if you’re building complex UIs with multiple conditional branches, you can accidentally reset state when toggling between branches.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;The Reality — SwiftUI’s View Identity&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;SwiftUI views are &lt;strong&gt;structs&lt;/strong&gt;. That means they are &lt;strong&gt;value types&lt;/strong&gt; — new copies get created constantly.  &lt;/p&gt;

&lt;p&gt;So if &lt;code&gt;@State&lt;/code&gt; really lived &lt;em&gt;inside&lt;/em&gt; your view struct, you’d lose your data every time the view refreshed.  &lt;/p&gt;

&lt;p&gt;The trick?&lt;br&gt;&lt;br&gt;
&lt;code&gt;@State&lt;/code&gt; stores your data &lt;strong&gt;outside&lt;/strong&gt; the view — in SwiftUI’s internal storage system — and links it to your view via &lt;strong&gt;identity&lt;/strong&gt;.&lt;/p&gt;


&lt;h2&gt;
  
  
  &lt;strong&gt;How Identity Works&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;When SwiftUI renders a view:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It checks if the “new” view is the same &lt;strong&gt;identity&lt;/strong&gt; as the previous one.
&lt;/li&gt;
&lt;li&gt;If yes, it reuses the same &lt;code&gt;@State&lt;/code&gt; storage.
&lt;/li&gt;
&lt;li&gt;If no, it throws away the old storage and starts fresh.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Identity is determined by:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;View type&lt;/li&gt;
&lt;li&gt;Position in the view hierarchy&lt;/li&gt;
&lt;li&gt;Any explicit &lt;code&gt;.id(...)&lt;/code&gt; you set&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Why It Matters for View Identity&lt;br&gt;
State storage is tied to the identity of the view in the view hierarchy, not the variable name.&lt;br&gt;
If SwiftUI thinks your view is different (e.g., changes id, order in the hierarchy, or parent view type), it will:&lt;/p&gt;
&lt;h2&gt;
  
  
  ** Tear down the old storage **
&lt;/h2&gt;

&lt;p&gt;Create new storage (resetting your @State)&lt;br&gt;
Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;toggle&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;MyView&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;// gets a new @State storage when toggle changes&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;MyView&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;// different identity =&amp;gt; state resets&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Fix: Give views a stable identity with .id(someStableValue) if you want state to persist.&lt;/p&gt;

&lt;p&gt;That’s why reordering, wrapping in new containers, or changing &lt;code&gt;.id&lt;/code&gt; can reset your &lt;code&gt;@State&lt;/code&gt;.------&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;4. Where Does It Physically Live in Memory?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;@State&lt;/code&gt; wrapper conforms to &lt;code&gt;DynamicProperty&lt;/code&gt;.  &lt;/p&gt;

&lt;p&gt;When compiled, SwiftUI injects a hidden &lt;code&gt;_State&lt;/code&gt; struct that references a &lt;code&gt;StateStorage&lt;/code&gt; object in the heap. That storage is owned and tracked by the SwiftUI runtime.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your View struct → &lt;strong&gt;on the stack&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Your &lt;code&gt;@State&lt;/code&gt; data → &lt;strong&gt;on the heap&lt;/strong&gt; (safe from constant view re-creations)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqlp3681bpba79klkvk1e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqlp3681bpba79klkvk1e.png" alt=" " width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;5. Real-Time Issues You’ll Actually Hit&lt;/strong&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1️⃣ Data Reset on Navigation
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kt"&gt;NavigationLink&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Open Detail"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;destination&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;DetailView&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If &lt;code&gt;DetailView&lt;/code&gt; has &lt;code&gt;@State&lt;/code&gt; and you navigate away &amp;amp; back — boom, your data resets.&lt;br&gt;&lt;br&gt;
Why? New view identity each time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; Use &lt;code&gt;@StateObject&lt;/code&gt; or pass the data from a higher-level view.&lt;/p&gt;

&lt;h3&gt;
  
  
  2️⃣ State Not Updating
&lt;/h3&gt;

&lt;p&gt;If your state depends on something that changes but your view identity stays constant, SwiftUI won’t refresh.&lt;br&gt;&lt;br&gt;
Sometimes you need &lt;code&gt;.id(someValue)&lt;/code&gt; to force identity change.&lt;/p&gt;

&lt;h3&gt;
  
  
  3️⃣ Multiple Copies of State
&lt;/h3&gt;

&lt;p&gt;Put a view with &lt;code&gt;@State&lt;/code&gt; inside a &lt;code&gt;List&lt;/code&gt;? Each row gets its &lt;strong&gt;own&lt;/strong&gt; copy of state, tied to its own identity.&lt;br&gt;&lt;br&gt;
Reordering rows? State might shuffle or reset.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;6. How to Think About &lt;code&gt;@State&lt;/code&gt;&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;If UIKit was:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“I’ll hold onto this reference until you tell me otherwise,”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;SwiftUI with &lt;code&gt;@State&lt;/code&gt; is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“I’ll keep this value alive for as long as your view’s identity stays in the tree — but break that link, and I’m tossing it.”&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;Key Takeaways&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Rules of Thumb&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Use &lt;code&gt;@State&lt;/code&gt; for simple, view-local, short-lived data
&lt;/li&gt;
&lt;li&gt;If data must survive view re-creations, lift it up or use &lt;code&gt;@StateObject&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Be careful with &lt;code&gt;.id(...)&lt;/code&gt; — it can reset your state
&lt;/li&gt;
&lt;li&gt;Remember: position in the view tree matters
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;code&gt;@State&lt;/code&gt; is not a normal property — it’s SwiftUI’s way of &lt;strong&gt;outsourcing your data to an identity-bound container&lt;/strong&gt;.&lt;br&gt;&lt;br&gt;
If your view’s identity changes, your &lt;code&gt;@State&lt;/code&gt; starts fresh.  &lt;/p&gt;

&lt;p&gt;Once you truly understand that, you’ll stop fighting SwiftUI and start making it work for you.&lt;/p&gt;




</description>
      <category>ios</category>
      <category>mobile</category>
      <category>swift</category>
      <category>swiftui</category>
    </item>
    <item>
      <title>What Really Happens When You Call an async Function in Swift?</title>
      <dc:creator>Vinayak G Hejib</dc:creator>
      <pubDate>Tue, 29 Jul 2025 11:16:11 +0000</pubDate>
      <link>https://dev.to/vnayak_hejib/what-really-happens-when-you-call-an-async-function-in-swift-5bo0</link>
      <guid>https://dev.to/vnayak_hejib/what-really-happens-when-you-call-an-async-function-in-swift-5bo0</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Calling await in Swift feels like casting a spell — except Swift is the sorcerer and you're just waving a wand.&lt;br&gt;
And like any good spell, there's a secret potion brewing underneath.&lt;br&gt;
We’ve all been there — late-night debugging, coffee in hand, watching your async function freeze and unfreeze like some kind of dark magic!!"&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Swift’s &lt;code&gt;async/await&lt;/code&gt; model makes asynchronous code elegant, readable, and non-blocking. But if you've ever paused mid-debug and wondered:&lt;br&gt;&lt;br&gt;
&lt;strong&gt;“Wait... where did my function actually &lt;em&gt;go&lt;/em&gt; after I hit &lt;code&gt;await&lt;/code&gt;?”&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
—you’re in the right place.&lt;/p&gt;

&lt;p&gt;This blog post takes you &lt;strong&gt;behind the scenes&lt;/strong&gt; of Swift's async functions, demystifying what really happens when you call them; underneath that calm, elegant surface is a full-blown circus of continuations, task schedulers, and a meticulously crafted state machine — all generated for you by the Swift compiler.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Let us consider as an example:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;When you write something like &lt;code&gt;await fetchItems()&lt;/code&gt;, it feels like magic. But behind the curtain, Swift is doing a whole lot of heavy lifting to make &lt;code&gt;async/await&lt;/code&gt; feel seamless — while keeping your app smooth and responsive. Let’s dive into how async functions really work in Swift, what makes them efficient, and why they’re much more than just syntactic sugar over callbacks.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/blockquote&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkvtttxd42x2e9j56udeg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkvtttxd42x2e9j56udeg.png" alt=" " width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At first glance, async functions look like ordinary sequential code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetchUser&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;posts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetchPosts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;for&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To you, the developer, it reads top-to-bottom — just like synchronous code.&lt;/p&gt;

&lt;p&gt;But in reality, every time you hit an &lt;code&gt;await&lt;/code&gt;, Swift pauses the function, hands over control to something else (like the main run loop), and resumes it later when the awaited value becomes available. This is more like a theatrical intermission than a simple pause.&lt;/p&gt;




&lt;h2&gt;
  
  
  Continuations: The Brain 🧠 Behind &lt;code&gt;await&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Swift uses a powerful concept called &lt;strong&gt;continuations&lt;/strong&gt;. Think of a continuation as a &lt;em&gt;"bookmark"&lt;/em&gt; for the current point in a function — it stores:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The function's local variables,&lt;/li&gt;
&lt;li&gt;The point where execution left off,&lt;/li&gt;
&lt;li&gt;And the next instruction to run when the awaited task completes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So when you write:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;profile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetchUserProfile&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Swift compiles this into a state machine that can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Suspend itself,&lt;/li&gt;
&lt;li&gt;Store context,&lt;/li&gt;
&lt;li&gt;And resume from exactly where it left off.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In lower-level terms, Swift transforms your async function into something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;enum&lt;/span&gt; &lt;span class="kt"&gt;FetchState&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;waitingForProfile&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;done&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This machinery is automatically generated during compilation — so your async function becomes a state machine under the hood, but you don’t have to manage that complexity manually.&lt;/p&gt;




&lt;h2&gt;
  
  
  Under the Hood: Swift’s Task System
&lt;/h2&gt;

&lt;p&gt;Behind all of this is Swift’s &lt;strong&gt;structured concurrency model&lt;/strong&gt;, built on top of a cooperative task system.&lt;/p&gt;

&lt;p&gt;When you create a new async task:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kt"&gt;Task&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;loadData&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Swift:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Allocates memory for a new task object,&lt;/li&gt;
&lt;li&gt;Schedules it for execution on a global executor,&lt;/li&gt;
&lt;li&gt;Executes its body until the first &lt;code&gt;await&lt;/code&gt;,&lt;/li&gt;
&lt;li&gt;Then suspends it — saving its continuation.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Later, when the awaited result is ready, the continuation is resumed on the appropriate executor (often the main actor if you're on the UI thread).&lt;/p&gt;

&lt;p&gt;This model is &lt;strong&gt;non-blocking&lt;/strong&gt;, so multiple async tasks can be scheduled and paused concurrently — all without spawning heavyweight threads.&lt;/p&gt;




&lt;h2&gt;
  
  
  Async/await ≠ Threads
&lt;/h2&gt;

&lt;p&gt;This is worth highlighting: &lt;strong&gt;calling an async function does NOT create a new thread&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If you call &lt;code&gt;await fetchData()&lt;/code&gt; from the main thread, the function suspends itself without blocking — freeing up the thread to keep UI rendering smooth. When the awaited result is available, Swift resumes the function on the appropriate thread or executor.&lt;/p&gt;

&lt;p&gt;This makes async/await an extremely lightweight and efficient model — far better than the GCD + callbacks spaghetti many iOS developers had to deal with in the past.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Magic of &lt;code&gt;await&lt;/code&gt; in SwiftUI
&lt;/h2&gt;

&lt;p&gt;If you’re using SwiftUI and structured concurrency, all of this allows you to write elegant code like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;@MainActor&lt;/span&gt;
&lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isLoading&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;networkService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetchData&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;
    &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isLoading&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Thanks to the underlying continuation-based machinery, you don’t have to think about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Dispatch queues,&lt;/li&gt;
&lt;li&gt;Completion handlers,&lt;/li&gt;
&lt;li&gt;Thread safety.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Swift takes care of it. But knowing how it works under the hood can help you reason about bugs, optimize performance, and understand suspension points better.&lt;/p&gt;




&lt;h2&gt;
  
  
  Bonus: Checked Continuations
&lt;/h2&gt;

&lt;p&gt;Sometimes you need to bridge old completion-based APIs with Swift concurrency. That’s where &lt;strong&gt;checked continuations&lt;/strong&gt; come in:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;legacyLogin&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;throws&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;User&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;withCheckedThrowingContinuation&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;continuation&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;
        &lt;span class="n"&gt;legacyAuthService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;login&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;
            &lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;success&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;user&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                &lt;span class="n"&gt;continuation&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resume&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;returning&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;failure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;error&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                &lt;span class="n"&gt;continuation&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resume&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;throwing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This allows you to “manually” capture and resume the continuation — but it also includes &lt;strong&gt;safety checks&lt;/strong&gt; to prevent misuse (e.g., multiple resumes, never-resumed bugs).&lt;/p&gt;




&lt;h2&gt;
  
  
  🧵 Wrapping Up: Async as State Machines
&lt;/h2&gt;

&lt;p&gt;To recap, when you write an async function in Swift:&lt;/p&gt;

&lt;p&gt;✅ It’s compiled into a state machine that can suspend and resume.&lt;br&gt;&lt;br&gt;
✅ Suspension happens via continuations stored in memory.&lt;br&gt;&lt;br&gt;
✅ Async functions don’t block threads — they’re suspended cooperatively.&lt;br&gt;&lt;br&gt;
✅ Swift’s structured concurrency handles lifecycle, error propagation, and cancellation for you.&lt;/p&gt;

&lt;p&gt;Knowing this helps you appreciate the performance, safety, and elegance Swift offers — and gives you the tools to write more thoughtful concurrent code.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiv2rws8v6wz4kway46vd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiv2rws8v6wz4kway46vd.png" alt=" " width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The image visualizes key concepts like:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Suspension points as space stations where the function can pause and resume&lt;/li&gt;
&lt;li&gt;The main thread continuing to handle other tasks (like UI updates)&lt;/li&gt;
&lt;li&gt;Task bubbles representing concurrent work happening in the background&lt;/li&gt;
&lt;li&gt;Swift's cooperative nature where async functions play nicely with the system&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  💡 Final Thought
&lt;/h2&gt;

&lt;p&gt;Next time you type &lt;code&gt;await&lt;/code&gt;, remember: you’re not pausing the universe — you’re crafting a finely orchestrated performance of mini-intermissions, backstage crew (executors), and set changes (continuations) that bring modern concurrency to life.&lt;/p&gt;

&lt;p&gt;Thanks for reading :)&lt;/p&gt;

</description>
    </item>
    <item>
      <title>The Art of Dependency Injection in SwiftUI</title>
      <dc:creator>Vinayak G Hejib</dc:creator>
      <pubDate>Mon, 21 Jul 2025 10:46:15 +0000</pubDate>
      <link>https://dev.to/vnayak_hejib/the-art-of-dependency-injection-in-swiftui-3fcd</link>
      <guid>https://dev.to/vnayak_hejib/the-art-of-dependency-injection-in-swiftui-3fcd</guid>
      <description>&lt;h3&gt;
  
  
  How I Stopped Worrying and Learned to Love Passing Stuff Around*
&lt;/h3&gt;

&lt;p&gt;If SwiftUI had a motto, it might be: &lt;em&gt;“Less is more, but good luck injecting that API client.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmpyqv6uv1r2x5v4q2cn3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmpyqv6uv1r2x5v4q2cn3.png" alt=" " width="800" height="1474"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  In the world of SwiftUI, dependency injection is like dating: you want clarity, low maintenance, and definitely no surprises. Whether you're passing view models, shared state, or static configuration, how you inject those dependencies can make or break your architecture — and your sanity.
&lt;/h4&gt;

&lt;p&gt;In this post, we’ll explore &lt;strong&gt;three elegant ways&lt;/strong&gt; to inject dependencies into SwiftUI views and when to use (or avoid) each:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt; Constructor-based injection
&lt;/li&gt;
&lt;li&gt; &lt;code&gt;@Environment&lt;/code&gt;-based injection with custom keys
&lt;/li&gt;
&lt;li&gt; &lt;code&gt;@EnvironmentObject&lt;/code&gt; for shared observable state
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We’ll use a fun example: a &lt;strong&gt;theme-aware counter screen&lt;/strong&gt;. No analytics, no token managers — just a beautiful button and a splash of color.&lt;/p&gt;




&lt;h2&gt;
  
  
  1️⃣ Constructor-based Injection: The “Polite Guest” Approach
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;struct&lt;/span&gt; &lt;span class="kt"&gt;Theme&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Color&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;textColor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Color&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;struct&lt;/span&gt; &lt;span class="kt"&gt;CounterView&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;View&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;@State&lt;/span&gt; &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Theme&lt;/span&gt;

    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;some&lt;/span&gt; &lt;span class="kt"&gt;View&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;VStack&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kt"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Count: &lt;/span&gt;&lt;span class="se"&gt;\(&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;foregroundColor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;theme&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;textColor&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="kt"&gt;Button&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Increment"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;background&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;theme&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;background&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cornerRadius&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  When to use it??:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;You value &lt;strong&gt;clarity and control&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;You love previews and &lt;strong&gt;unit testing without wizardry&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Your theme shouldn't mysteriously change mid-scroll&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  2️⃣ Environment Injection: &lt;em&gt;The Magical Air You Breathe&lt;/em&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1: Define a Custom Environment Key
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;struct&lt;/span&gt; &lt;span class="kt"&gt;ThemeKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;EnvironmentKey&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;defaultValue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Theme&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;white&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;textColor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;black&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;extension&lt;/span&gt; &lt;span class="kt"&gt;EnvironmentValues&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Theme&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;get&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;ThemeKey&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;set&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;ThemeKey&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;newValue&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: Inject It Once, Use It Anywhere
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;struct&lt;/span&gt; &lt;span class="kt"&gt;CounterView&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;View&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;@Environment&lt;/span&gt;&lt;span class="p"&gt;(\&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;theme&lt;/span&gt;
    &lt;span class="kd"&gt;@State&lt;/span&gt; &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;some&lt;/span&gt; &lt;span class="kt"&gt;View&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;VStack&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kt"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Count: &lt;/span&gt;&lt;span class="se"&gt;\(&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;foregroundColor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;theme&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;textColor&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="kt"&gt;Button&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Increment"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;background&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;theme&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;background&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cornerRadius&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3: Set the Theme from a Parent
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;struct&lt;/span&gt; &lt;span class="kt"&gt;RootView&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;View&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;some&lt;/span&gt; &lt;span class="kt"&gt;View&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;CounterView&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;(\&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Theme&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mint&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;textColor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;indigo&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  When to use it??:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Your dependency is &lt;strong&gt;lightweight and stable&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;You don't want to &lt;strong&gt;manually pass values 10 levels deep&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;You love magic that comes with a fallback value&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  3️⃣ &lt;code&gt;@EnvironmentObject&lt;/code&gt;: The Loud Roommate
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="kt"&gt;ThemeSettings&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;ObservableObject&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;@Published&lt;/span&gt; &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;theme&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Theme&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;white&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;textColor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;black&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;struct&lt;/span&gt; &lt;span class="kt"&gt;CounterView&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;View&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;@EnvironmentObject&lt;/span&gt; &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;ThemeSettings&lt;/span&gt;
    &lt;span class="kd"&gt;@State&lt;/span&gt; &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;some&lt;/span&gt; &lt;span class="kt"&gt;View&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;VStack&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kt"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Count: &lt;/span&gt;&lt;span class="se"&gt;\(&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;foregroundColor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;theme&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;textColor&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="kt"&gt;Button&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Increment"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;background&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;theme&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;background&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cornerRadius&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Inject it globally:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;@main&lt;/span&gt;
&lt;span class="kd"&gt;struct&lt;/span&gt; &lt;span class="kt"&gt;MyApp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;App&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;@StateObject&lt;/span&gt; &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;themeSettings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;ThemeSettings&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;some&lt;/span&gt; &lt;span class="kt"&gt;Scene&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;WindowGroup&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kt"&gt;CounterView&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;environmentObject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;themeSettings&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  When to use it??:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;You need &lt;strong&gt;observable shared state&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;You don’t mind &lt;strong&gt;runtime crashes when someone forgets &lt;code&gt;.environmentObject(...)&lt;/code&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;You like living on the edge&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  ⚖️ Environment Key vs EnvironmentObject: Explained with Snacks
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;You have a 🍪 (biscuit)&lt;/th&gt;
&lt;th&gt;Use &lt;code&gt;@Environment&lt;/code&gt;
&lt;/th&gt;
&lt;th&gt;Use &lt;code&gt;@EnvironmentObject&lt;/code&gt;
&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;The 🍪 never changes and can be safely shared&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;The 🍪 might change (someone might take a bite)&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;You want a default fallback 🍪&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;You forget to bring a 🍪 and the app crashes&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Summary: Which Flavor to Choose?
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Technique&lt;/th&gt;
&lt;th&gt;Best For&lt;/th&gt;
&lt;th&gt;Avoid If...&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Constructor Injection&lt;/td&gt;
&lt;td&gt;Explicit, testable setup&lt;/td&gt;
&lt;td&gt;You hate writing initializers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;@Environment&lt;/code&gt; + Key&lt;/td&gt;
&lt;td&gt;Global/static dependencies (themes)&lt;/td&gt;
&lt;td&gt;You need reactivity&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@EnvironmentObject&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Shared state that updates views&lt;/td&gt;
&lt;td&gt;You need compile-time guarantees&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Final Thought
&lt;/h2&gt;

&lt;p&gt;SwiftUI gives you powerful, expressive tools for managing dependencies — just enough structure to keep your code clean, and just enough magic to feel ✨Swifty✨.&lt;/p&gt;

&lt;p&gt;So the next time you’re passing a &lt;code&gt;Theme&lt;/code&gt;, a &lt;code&gt;Settings&lt;/code&gt; object, or even a metaphorical biscuit 🍪, ask yourself:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Does this need to be shared?&lt;/li&gt;
&lt;li&gt;Does it change?&lt;/li&gt;
&lt;li&gt;Do I want control or convenience?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Because dependency injection in SwiftUI is like parenting: it’s all about &lt;strong&gt;boundaries, visibility, and who gets to press the increment button.&lt;/strong&gt; 😉&lt;/p&gt;

</description>
      <category>ios</category>
      <category>swiftui</category>
      <category>programming</category>
      <category>mobile</category>
    </item>
    <item>
      <title>iOS Apps That Don’t Crash: Myth or Method?</title>
      <dc:creator>Vinayak G Hejib</dc:creator>
      <pubDate>Tue, 08 Jul 2025 10:00:08 +0000</pubDate>
      <link>https://dev.to/vnayak_hejib/ios-apps-that-dont-crash-myth-or-method-5gj8</link>
      <guid>https://dev.to/vnayak_hejib/ios-apps-that-dont-crash-myth-or-method-5gj8</guid>
      <description>&lt;h1&gt;
  
  
  💥
&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;“An app crash is the loudest silence your user will ever hear.”&lt;/em&gt;&lt;br&gt;
— Every iOS engineer, at some point (At-least I am!!).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Nothing frustrates users more than an unexpected crash. That moment when your app just… disappears. It’s not just bad UX — it’s a trust breaker. While some crashes are inevitable, many are preventable, especially with the power of modern Swift, improved tooling, and a proactive mindset.&lt;/p&gt;

&lt;p&gt;In this post, we’ll walk through the &lt;em&gt;types of iOS crashes&lt;/em&gt;, &lt;em&gt;how to prevent them with Swift features&lt;/em&gt;, and &lt;em&gt;how to detect and fix them&lt;/em&gt; — ideally &lt;em&gt;before&lt;/em&gt; your code hits production.&lt;/p&gt;




&lt;h2&gt;
  
  
  ⚙️ Why Apps Crash on iOS: Common Culprits
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. &lt;strong&gt;Nil Optionals (Force Unwrapping Gone Rogue)&lt;/strong&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;nil&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// 💥 crash: Unexpectedly found nil&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. &lt;strong&gt;Out-of-Bounds Access&lt;/strong&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;items&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"Apple"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Banana"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;item&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="c1"&gt;// 💥 crash: Index out of range&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. &lt;strong&gt;Uncaught Exceptions (NSException)&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Some Apple APIs (e.g., KVO, CoreData) throw Objective-C-style exceptions — which Swift doesn't catch.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. &lt;strong&gt;Memory Issues / Retain Cycles&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Retain cycles in closures or view models can cause memory bloat → crashes.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. &lt;strong&gt;Main Thread Violations&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Doing UI updates off the main thread can lead to subtle and dangerous bugs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kt"&gt;DispatchQueue&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;global&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;someUILabel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Oops"&lt;/span&gt; &lt;span class="c1"&gt;// 💥 boom on a bad day&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🛡️ Prevention: Write Crash-Resistant Swift
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Safe Optional Handling
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getUserName&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Name not available"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;getUserName&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;??&lt;/span&gt; &lt;span class="s"&gt;"Guest"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. &lt;code&gt;guard&lt;/code&gt; For Early Exit
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;showUserProfile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="nv"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;User&lt;/span&gt;&lt;span class="p"&gt;?)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;guard&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;profile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;profile&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Invalid profile"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;profile&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Use &lt;code&gt;@MainActor&lt;/code&gt; to Protect UI Updates
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;@MainActor&lt;/span&gt;
&lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;updateUI&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Updated safely"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="kt"&gt;MainActor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Updated safely"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Weak Self in Closures
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="n"&gt;fetchData&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;weak&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;
    &lt;span class="k"&gt;guard&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;self&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;updateUI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;with&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  5. Use Value Types (Structs) Where Possible
&lt;/h3&gt;

&lt;p&gt;Classes can lead to retain cycles. Structs don't. Think in value semantics when you can.&lt;/p&gt;




&lt;h2&gt;
  
  
  🔍 Detecting Crashes Before They Happen
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. &lt;strong&gt;Xcode Runtime Sanitizers&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Enable these under:&lt;br&gt;
&lt;strong&gt;Scheme &amp;gt; Diagnostics &amp;gt; Enable Undefined Behavior Sanitizer / Thread Sanitizer / Address Sanitizer&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  2. &lt;strong&gt;Unit &amp;amp; UI Testing with XCTExpectFailure&lt;/strong&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;testInvalidIndexAccess&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;array&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="kt"&gt;XCTExpectFailure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Array index out of bounds"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. &lt;strong&gt;Use Static Analyzers &amp;amp; Linting&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;SwiftLint&lt;/strong&gt;: Catch unsafe force unwraps and patterns&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SwiftFormat&lt;/strong&gt;: Enforces code style that avoids dangerous patterns&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Xcode Analyzer&lt;/strong&gt;: Shift + Cmd + B = your crash prevention friend&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4. &lt;strong&gt;Crash Monitoring in QA&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Use tools like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Firebase Crashlytics&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Instabug&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Sentry&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;BugSnag&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Deploy to internal TestFlight groups or dogfood builds with crash tracking enabled.&lt;/p&gt;




&lt;h2&gt;
  
  
  📦 Diagnosing Production Crashes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  🔹 Use Symbolicated Stack Traces
&lt;/h3&gt;

&lt;p&gt;Without dSYMs, crash reports are useless. Xcode, Bitrise, or Firebase can automate dSYM upload.&lt;/p&gt;

&lt;h3&gt;
  
  
  🔹 Add Custom Logs &amp;amp; Breadcrumbs
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kt"&gt;Crashlytics&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;crashlytics&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"User tapped Submit on Login"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  🔹 Detect and Auto-Recover
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="nf"&gt;riskyOperation&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;showAlert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Something went wrong. Please try again."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  ✨ Bonus: Crash Prevention Design Patterns
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Pattern&lt;/th&gt;
&lt;th&gt;What it Helps Prevent&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;MVVM / Clean Architecture&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Logic separation reduces side-effects&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Dependency Injection&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Easier testing and mocking prevents runtime surprises&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Result Type over Throw&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Encourages developers to handle errors explicitly&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Combine / async-await&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Replaces callback pyramids and reduces state explosion&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Real-World Pro Tips
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Use &lt;strong&gt;assertion failures&lt;/strong&gt; in dev builds to make bugs visible early.&lt;/li&gt;
&lt;li&gt;Treat &lt;strong&gt;force-unwrapping (&lt;code&gt;!&lt;/code&gt;) as a code smell&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Add &lt;strong&gt;crash reproducer tests&lt;/strong&gt; for every known crash postmortem.&lt;/li&gt;
&lt;li&gt;Schedule &lt;strong&gt;crash triage sessions&lt;/strong&gt; for new releases.&lt;/li&gt;
&lt;li&gt;Don't ignore "minor" crashes — they're often just the tip of the iceberg.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🌐 Bonus: Integrate Crash Analytics Proactively
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Example: Firebase Crashlytics Integration
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;import&lt;/span&gt; &lt;span class="kt"&gt;FirebaseCrashlytics&lt;/span&gt;

&lt;span class="kt"&gt;Crashlytics&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;crashlytics&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Login button tapped"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kt"&gt;Crashlytics&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;crashlytics&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setCustomValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;forKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kt"&gt;Crashlytics&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;crashlytics&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;record&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;MyAppError&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;someUnexpectedError&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  📘 Useful Swift Tips to Remember
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Pattern&lt;/th&gt;
&lt;th&gt;Tip&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;!&lt;/code&gt; (force unwrap)&lt;/td&gt;
&lt;td&gt;Avoid unless you &lt;em&gt;guarantee&lt;/em&gt; non-nil&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;try!&lt;/code&gt; or &lt;code&gt;as!&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Use only in tests or tightly controlled code&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;assert(Thread.isMainThread)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Ensure main-thread-only execution&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;guard let self else { return }&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Better in async closures than forced &lt;code&gt;self!&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Use &lt;code&gt;struct&lt;/code&gt; over &lt;code&gt;class&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Value types reduce shared state crashes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  🚨 Real-World Anti-Patterns That Lead to Crashes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  ❌ The Overconfident Optional
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;image&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;UIImage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;named&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"iconName"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  ❌ The Zombie Delegate
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="n"&gt;delegate&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;didTapButton&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;// crash if delegate was deallocated&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  ❌ Thread Roulette
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kt"&gt;DispatchQueue&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;global&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tableView&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reloadData&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🔺 Wrapping Up: Build With Crashes in Mind
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Key Takeaways:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt; Modern Swift lets you write defensive, crash-resistant code.&lt;/li&gt;
&lt;li&gt; Use runtime tools, sanitizers, and test coverage to catch crashes early.&lt;/li&gt;
&lt;li&gt; Crash reporting is &lt;em&gt;not optional&lt;/em&gt; in production-grade apps.&lt;/li&gt;
&lt;li&gt; Your users won’t always leave reviews, but they will remember a crash.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Write code like you're the one waking up at 3AM to fix the crash report.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Your Crash Audit Checklist should look like:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt; Are all optionals safely unwrapped?&lt;/li&gt;
&lt;li&gt; Are UI updates happening only on the main thread?&lt;/li&gt;
&lt;li&gt; Are closures using &lt;code&gt;[weak self]&lt;/code&gt; properly?&lt;/li&gt;
&lt;li&gt; Are crash reporting tools integrated and tested?&lt;/li&gt;
&lt;li&gt; Do you have test coverage for both happy and failure paths?&lt;/li&gt;
&lt;li&gt; Are your crash logs symbolicated and actionable?&lt;/li&gt;
&lt;li&gt; Did you simulate edge cases like low memory, network unavailability, and nil values?&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;If you’ve made it this far, you truly care about app’s reliability. Thanks for reading.. :)&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Modernizing a Legacy Modular iOS Codebase</title>
      <dc:creator>Vinayak G Hejib</dc:creator>
      <pubDate>Mon, 23 Jun 2025 13:46:03 +0000</pubDate>
      <link>https://dev.to/vnayak_hejib/modernizing-a-legacy-modular-ios-codebase-3m10</link>
      <guid>https://dev.to/vnayak_hejib/modernizing-a-legacy-modular-ios-codebase-3m10</guid>
      <description>&lt;h1&gt;
  
  
  SwiftUI Migration, Scalable Architecture &amp;amp; Seamless Testability
&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;"You don’t rewrite a legacy codebase. You &lt;strong&gt;rearchitect&lt;/strong&gt; it, one module at a time."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In this guide, we break down how we can transform a large-scale, UIKit-based modular monolith into a SwiftUI-driven, testable, maintainable codebase with a clean architecture. The transformation touched every part of the stack: from UI to API, from navigation to build systems.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;Lets say, over time, your iOS app evolved into a &lt;strong&gt;monolithic modular&lt;/strong&gt; architecture:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Organized by feature folders (e.g., Home, Profile, Login).&lt;/li&gt;
&lt;li&gt;Common modules like Networking, Models, Storage.&lt;/li&gt;
&lt;li&gt;Heavy reliance on UIKit, Storyboards, and XIBs.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Pain Points:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Bloated ViewControllers&lt;/strong&gt;: Handling UI, navigation, API, caching all at once.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tight Coupling&lt;/strong&gt;: Feature modules often depended on each other directly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Unscalable Navigation&lt;/strong&gt;: Navigation logic spread across multiple layers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Poor Testability&lt;/strong&gt;: Difficult to write unit or integration tests due to side effects.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Inconsistent Architecture&lt;/strong&gt;: Teams used different patterns (MVC, MVVM, Singleton-based flows).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Slow Build Times&lt;/strong&gt;: Everything compiled in one target; CI times increased.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We need a &lt;strong&gt;modern, scalable, testable, and maintainable&lt;/strong&gt; solution.&lt;/p&gt;




&lt;h2&gt;
  
  
  🛠 Step 1: Choosing the Right Architecture
&lt;/h2&gt;

&lt;p&gt;After reviewing several architecture styles (VIPER, RIBs, Clean, TCA), I would prefer:&lt;/p&gt;

&lt;h3&gt;
  
  
  ✅ MVVM + Coordinators + Dependency Injection
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Why?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fits SwiftUI's declarative nature&lt;/li&gt;
&lt;li&gt;ViewModels hold business logic and state&lt;/li&gt;
&lt;li&gt;Coordinators isolate navigation, making it reusable and testable&lt;/li&gt;
&lt;li&gt;Dependency Injection enables test mocking and clear boundaries&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Swift Package Manager (SPM) for Modularization
&lt;/h3&gt;

&lt;p&gt;Use SPM to break our app into independently buildable, testable components.&lt;/p&gt;




&lt;h2&gt;
  
  
  📂 Step 2: Modularization Strategy
&lt;/h2&gt;

&lt;p&gt;Inspired by &lt;a href="https://vbat.dev/how-to-modularize-monolith-ios-app" rel="noopener noreferrer"&gt;vbat.dev&lt;/a&gt;, we defined modules by &lt;strong&gt;feature&lt;/strong&gt; and &lt;strong&gt;responsibility&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Types of Modules:
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;Modules&lt;/th&gt;
&lt;th&gt;Examples&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Feature&lt;/td&gt;
&lt;td&gt;HomeFeature, SearchFeature, CartFeature&lt;/td&gt;
&lt;td&gt;Business logic + UI&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Core&lt;/td&gt;
&lt;td&gt;CoreNetworking, CoreModels, CoreStorage&lt;/td&gt;
&lt;td&gt;Reusable low-level components&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;UI&lt;/td&gt;
&lt;td&gt;SharedUI, DesignSystem&lt;/td&gt;
&lt;td&gt;Custom buttons, theme, reusable views&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Infra&lt;/td&gt;
&lt;td&gt;Analytics, CrashReporting&lt;/td&gt;
&lt;td&gt;Services, APIs, Gateways&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;Rule: &lt;strong&gt;Feature modules should only depend on Core or Infra. Never on each other.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Structure Example:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/Packages
  /Features
    /HomeFeature
  /Core
    /CoreNetworking
    /CoreModels
  /UI
    /SharedUI
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 3: Refactor the Core First
&lt;/h2&gt;

&lt;p&gt;Before migrating UI or adding SwiftUI, we started with &lt;strong&gt;refactoring our core modules&lt;/strong&gt;, especially &lt;code&gt;CoreNetworking&lt;/code&gt; and &lt;code&gt;CoreModels&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why Start with Core?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Core defines &lt;strong&gt;how features interact with data and services&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Consistent APIs and dependency rules enable scalable adoption&lt;/li&gt;
&lt;li&gt;Core is reused across all features — enforcing good practices here radiates out&lt;/li&gt;
&lt;li&gt;Encourages a &lt;strong&gt;decoupled, protocol-first design&lt;/strong&gt; supporting testability and flexibility&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  CoreNetworking Redesign Goals:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Define API requests using protocols&lt;/strong&gt; (&lt;code&gt;APIRequest&lt;/code&gt;, &lt;code&gt;APIClientProtocol&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Use &lt;strong&gt;Result-based decoding pipelines&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Make all requests async and testable&lt;/li&gt;
&lt;li&gt;Provide &lt;strong&gt;mockable interfaces&lt;/strong&gt; for all networking services&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Example:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;protocol&lt;/span&gt; &lt;span class="kt"&gt;APIRequest&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;associatedtype&lt;/span&gt; &lt;span class="kt"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Decodable&lt;/span&gt;
    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;HTTPMethod&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;protocol&lt;/span&gt; &lt;span class="kt"&gt;APIClientProtocol&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="n"&gt;send&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;T&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;APIRequest&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="nv"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;T&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;throws&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;T&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Response&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="kt"&gt;APIClient&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;APIClientProtocol&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="n"&gt;send&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="nv"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;T&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;throws&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;T&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Response&lt;/span&gt; &lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="kt"&gt;T&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;APIRequest&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Build URLRequest, perform, decode&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Result:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;ViewModels across features now depend only on &lt;code&gt;APIClientProtocol&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Services (e.g., &lt;code&gt;HomeService&lt;/code&gt;) use these APIs internally and are &lt;strong&gt;easy to stub in tests&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Unified patterns help enforce clean architecture across teams&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  📈 Enhancing CoreModels and CoreStorage
&lt;/h2&gt;

&lt;h3&gt;
  
  
  DTO vs. Domain Model
&lt;/h3&gt;

&lt;p&gt;To maintain separation between networking layers and domain logic, we used the DTO → DomainModel transformation pattern:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3h8dkglwbk38ic2l5jl4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3h8dkglwbk38ic2l5jl4.png" alt=" " width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgodzc3edkb18fepc1jzm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgodzc3edkb18fepc1jzm.png" alt=" " width="800" height="931"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This allows our ViewModels and business logic to work with clean, decoupled domain objects while networking remains isolated.&lt;/p&gt;

&lt;p&gt;Beyond Networking, we standardized &lt;code&gt;CoreModels&lt;/code&gt; and &lt;code&gt;CoreStorage&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Models conform to &lt;code&gt;Codable&lt;/code&gt; and are shared across modules.&lt;/li&gt;
&lt;li&gt;Use of &lt;code&gt;DomainModel&lt;/code&gt; vs &lt;code&gt;DTO&lt;/code&gt; separation helped decouple layers.&lt;/li&gt;
&lt;li&gt;Abstracted UserDefaults, Keychain, and FileStorage under unified protocols:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;protocol&lt;/span&gt; &lt;span class="kt"&gt;KeyValueStoring&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;T&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Decodable&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="nv"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;T&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;
    &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;T&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Encodable&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="nv"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;T&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nv"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Concrete implementations (e.g., &lt;code&gt;UserDefaultsStore&lt;/code&gt;, &lt;code&gt;MockStorage&lt;/code&gt;) live inside Core or TestTargets&lt;/li&gt;
&lt;li&gt;This made it easy to mock persistence during unit testing and eliminate side effects.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Tooling: Enforcing Architecture Contracts
&lt;/h2&gt;

&lt;p&gt;To scale this approach across teams, we adopted tools to enforce boundaries and consistency:&lt;/p&gt;

&lt;h3&gt;
  
  
  SwiftLint
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Custom rules to restrict module imports&lt;/li&gt;
&lt;li&gt;Enforce naming conventions (&lt;code&gt;*ViewModel.swift&lt;/code&gt;, &lt;code&gt;*Coordinator.swift&lt;/code&gt;, etc.)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Sourcery
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Auto-generate mocks for protocols using &lt;code&gt;// sourcery: AutoMockable&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Prevents writing tedious boilerplate for DI testing&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Tuist (or Bazel)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Automate SPM graph generation&lt;/li&gt;
&lt;li&gt;Validate dependency rules&lt;/li&gt;
&lt;li&gt;Scales well for large modular workspaces&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Step 4: Refactoring a Real Module - &lt;code&gt;HomeFeature&lt;/code&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Feature Module Dependency Graph
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2fjaxu75l00h3m35hiwp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2fjaxu75l00h3m35hiwp.png" alt=" " width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This diagram illustrates how each feature module (e.g., Home, Profile, Search) depends only on shared Core modules and not on each other, enforcing clean separation and reusability.&lt;/p&gt;

&lt;h3&gt;
  
  
  Original State:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;600+ LOC in &lt;code&gt;HomeViewController&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;UICollectionView + network fetches in the controller&lt;/li&gt;
&lt;li&gt;Logic for loading, caching, refresh, navigation all tightly coupled&lt;/li&gt;
&lt;li&gt;Hard to test; lots of UI-state bugs&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Goals:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Move UI to SwiftUI&lt;/li&gt;
&lt;li&gt;Extract logic to ViewModel&lt;/li&gt;
&lt;li&gt;Use Coordinator for navigation&lt;/li&gt;
&lt;li&gt;Inject dependencies (no singletons)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🔁 Step 5: Architecture in Action
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;HomeViewModel.swift&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;@MainActor&lt;/span&gt;
&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="kt"&gt;HomeViewModel&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;ObservableObject&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;@Published&lt;/span&gt; &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;HomeItem&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="kd"&gt;@Published&lt;/span&gt; &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;isLoading&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
    &lt;span class="kd"&gt;@Published&lt;/span&gt; &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;

    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;service&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;HomeServiceProtocol&lt;/span&gt;

    &lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;service&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;HomeServiceProtocol&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;service&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;service&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;fetchItems&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;isLoading&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
        &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;items&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetchItems&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;isLoading&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;HomeView.swift&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;struct&lt;/span&gt; &lt;span class="kt"&gt;HomeView&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;View&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;@ObservedObject&lt;/span&gt; &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;viewModel&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;HomeViewModel&lt;/span&gt;

    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;some&lt;/span&gt; &lt;span class="kt"&gt;View&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;NavigationStack&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kt"&gt;Group&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;viewModel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isLoading&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="kt"&gt;ProgressView&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;error&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;viewModel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="kt"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Error: &lt;/span&gt;&lt;span class="se"&gt;\(&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;localizedDescription&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="kt"&gt;List&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;viewModel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;
                        &lt;span class="kt"&gt;HomeRow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;item&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;navigationTitle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Home"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;onAppear&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kt"&gt;Task&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;viewModel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetchItems&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;HomeCoordinator.swift&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="kt"&gt;HomeCoordinator&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;ObservableObject&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;@Published&lt;/span&gt; &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;NavigationPath&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kd"&gt;some&lt;/span&gt; &lt;span class="kt"&gt;View&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;vm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;HomeViewModel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;service&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;HomeService&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kt"&gt;HomeView&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;viewModel&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;vm&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;environmentObject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;goToDetail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nv"&gt;item&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;HomeItem&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 6: Improving Testability
&lt;/h2&gt;

&lt;p&gt;By adopting dependency injection, protocol-first design, and clear ViewModel boundaries, our modules became much more testable.&lt;/p&gt;

&lt;h3&gt;
  
  
  Unit Test Example
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="kt"&gt;HomeViewModelTests&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;XCTestCase&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;testFetchItemsSuccess&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;mockService&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;MockHomeService&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;mockService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stubbedItems&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mock&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mock&lt;/span&gt;&lt;span class="p"&gt;()]&lt;/span&gt;

        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;vm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;HomeViewModel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;service&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;mockService&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;vm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetchItems&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="kt"&gt;XCTAssertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="kt"&gt;XCTAssertNil&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vm&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  SwiftUI Testing
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Use &lt;code&gt;.accessibilityIdentifier()&lt;/code&gt; to enable UI automation&lt;/li&gt;
&lt;li&gt;Leverage &lt;a href="https://github.com/nalexn/ViewInspector" rel="noopener noreferrer"&gt;ViewInspector&lt;/a&gt; for unit testing SwiftUI views&lt;/li&gt;
&lt;li&gt;Snapshot testing using &lt;code&gt;iOSSnapshotTestCase&lt;/code&gt; or &lt;code&gt;SwiftSnapshotTesting&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Step 7: CI, Build Times &amp;amp; Developer Experience
&lt;/h2&gt;

&lt;p&gt;Update CI pipelines and local builds to benefit from modularization:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SPM made incremental builds faster (~40% improvement)&lt;/li&gt;
&lt;li&gt;CI workflows ran unit tests per package&lt;/li&gt;
&lt;li&gt;Pre-commit linting and type checks prevented regressions&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Tooling Summary
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Use&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;SPM&lt;/td&gt;
&lt;td&gt;Modularized features and core logic&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SwiftLint&lt;/td&gt;
&lt;td&gt;Enforced import boundaries and naming rules&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h1&gt;
  
  
  Add on:
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Centralized Feature Initialization via a Builder Pattern
&lt;/h2&gt;

&lt;p&gt;In a modular SwiftUI codebase, it's essential to cleanly instantiate feature modules with their dependencies. Instead of creating each module inline or in scattered views, we can define a FeatureModuleBuilder that:&lt;/p&gt;

&lt;p&gt;Step 1: Define Core Services&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="c1"&gt;// CoreNetwork / CoreStorage&lt;/span&gt;
&lt;span class="kd"&gt;protocol&lt;/span&gt; &lt;span class="kt"&gt;APIClientProtocol&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="n"&gt;fetch&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;T&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Decodable&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="nv"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Endpoint&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;throws&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;T&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;protocol&lt;/span&gt; &lt;span class="kt"&gt;CacheManagerProtocol&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="nv"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Data&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;
    &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="nv"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nv"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Step 2: Define Feature Modules with Dependency Structs&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="c1"&gt;// HomeFeature/HomeDependencies.swift&lt;/span&gt;
&lt;span class="kd"&gt;struct&lt;/span&gt; &lt;span class="kt"&gt;HomeDependencies&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;apiClient&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;APIClientProtocol&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;cacheManager&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;CacheManagerProtocol&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// HomeFeature/HomeView.swift&lt;/span&gt;
&lt;span class="kd"&gt;struct&lt;/span&gt; &lt;span class="kt"&gt;HomeView&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;View&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;@StateObject&lt;/span&gt; &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;viewModel&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;HomeViewModel&lt;/span&gt;

    &lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;dependencies&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;HomeDependencies&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_viewModel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;StateObject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;wrappedValue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;HomeViewModel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nv"&gt;apiClient&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;dependencies&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;apiClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nv"&gt;cacheManager&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;dependencies&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cacheManager&lt;/span&gt;
        &lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;some&lt;/span&gt; &lt;span class="kt"&gt;View&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;List&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;viewModel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;\&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kt"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$0&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;task&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;viewModel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loadItems&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Step 3: Feature Module Builder&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="c1"&gt;// MainApp/FeatureModuleBuilder.swift&lt;/span&gt;
&lt;span class="kd"&gt;struct&lt;/span&gt; &lt;span class="kt"&gt;FeatureModuleBuilder&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;apiClient&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;APIClientProtocol&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;cacheManager&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;CacheManagerProtocol&lt;/span&gt;

    &lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;apiClient&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;APIClientProtocol&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;cacheManager&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;CacheManagerProtocol&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;apiClient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;apiClient&lt;/span&gt;
        &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cacheManager&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cacheManager&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;makeHomeView&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kd"&gt;some&lt;/span&gt; &lt;span class="kt"&gt;View&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;deps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;HomeDependencies&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;apiClient&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;apiClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;cacheManager&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;cacheManager&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kt"&gt;HomeView&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;dependencies&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;deps&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Add more feature modules here&lt;/span&gt;
    &lt;span class="c1"&gt;// func makeProfileView() -&amp;gt; some View { ... }&lt;/span&gt;
    &lt;span class="c1"&gt;// func makeSearchView() -&amp;gt; some View { ... }&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Step 4: RootCoordinator Using the Builder&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="c1"&gt;// MainApp/RootCoordinator.swift&lt;/span&gt;
&lt;span class="kd"&gt;struct&lt;/span&gt; &lt;span class="kt"&gt;RootCoordinator&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;View&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;FeatureModuleBuilder&lt;/span&gt;

    &lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;FeatureModuleBuilder&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;builder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;some&lt;/span&gt; &lt;span class="kt"&gt;View&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;makeHomeView&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Step 5: Final Entry Point&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="c1"&gt;// MainApp/MainApp.swift&lt;/span&gt;
&lt;span class="kd"&gt;@main&lt;/span&gt;
&lt;span class="kd"&gt;struct&lt;/span&gt; &lt;span class="kt"&gt;MainApp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;App&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;some&lt;/span&gt; &lt;span class="kt"&gt;Scene&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;apiClient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;DefaultAPIClient&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;cacheManager&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;InMemoryCacheManager&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;builder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;FeatureModuleBuilder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nv"&gt;apiClient&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;apiClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nv"&gt;cacheManager&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;cacheManager&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="kt"&gt;WindowGroup&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kt"&gt;RootCoordinator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Benefits of the Builder Pattern&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Centralized wiring of feature modules and dependencies&lt;/li&gt;
&lt;li&gt;Easy to extend when adding new modules&lt;/li&gt;
&lt;li&gt;Keeps SwiftUI views clean and declarative&lt;/li&gt;
&lt;li&gt;Test-friendly structure — builder can inject mocks during testing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Lives in the main app target&lt;br&gt;
Knows how to wire each feature with its required services&lt;br&gt;
Keeps dependency composition clean and maintainable&lt;/p&gt;

&lt;h2&gt;
  
  
  🛤 What’s Next?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Expand SwiftUI adoption across remaining modules&lt;/li&gt;
&lt;li&gt;Build cross-platform foundations (macOS, visionOS)&lt;/li&gt;
&lt;li&gt;Define shared state management (e.g., Combine, ObservableDomain)&lt;/li&gt;
&lt;li&gt;Explore performance gains with SwiftData + Swift Macros&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🔗 References &amp;amp; Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://vbat.dev/how-to-modularize-monolith-ios-app" rel="noopener noreferrer"&gt;vbat.dev – Modular Monolith&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medium.com/@GetInRhythm/modern-scalable-ios-architecture-building-for-the-future-4eb7d661e0b7" rel="noopener noreferrer"&gt;GetInRhythm iOS Architecture&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.apple.com/documentation/swift_packages" rel="noopener noreferrer"&gt;Swift Package Manager Docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/nalexn/ViewInspector" rel="noopener noreferrer"&gt;ViewInspector&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;blockquote&gt;
&lt;p&gt;Migrating to SwiftUI and modular architecture isn't just about code. It's about empowering teams to move faster, test better, and scale fearlessly.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
    </item>
  </channel>
</rss>
