<?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.us-east-2.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>The Next Evolution of App Architecture: AI Just Introduced a New Orchestrator.</title>
      <dc:creator>Vinayak G Hejib</dc:creator>
      <pubDate>Tue, 04 Aug 2026 16:19:18 +0000</pubDate>
      <link>https://dev.to/vnayak_hejib/the-next-evolution-of-app-architecture-ai-just-introduced-a-new-orchestrator-9na</link>
      <guid>https://dev.to/vnayak_hejib/the-next-evolution-of-app-architecture-ai-just-introduced-a-new-orchestrator-9na</guid>
      <description>&lt;h1&gt;
  
  
  Your architecture may not be dead but remember AI just introduced a new orchestrator
&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; AI doesn't replace MVVM or anything compliant to it. &lt;br&gt;
It introduces a new orchestration layer. ViewModels still excel at managing UI state, but when your app needs to reason, plan, use tools, and maintain context, an &lt;strong&gt;Agent&lt;/strong&gt; becomes a natural addition to your architecture.&lt;/p&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9bd2x6rm4lb0naal5ko4.webp" 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9bd2x6rm4lb0naal5ko4.webp" alt=" " width="800" height="1000"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The Shift We Didn't See Coming
&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ft11i5xsjt1co3ge2vfgv.webp" 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ft11i5xsjt1co3ge2vfgv.webp" alt=" " width="800" height="1000"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For over a decade, mobile architecture has focused on making UI code predictable and maintainable. Whether you follow MVC, MVP, VIPER, Clean Architecture, MVVM, or The Composable Architecture, the core idea has remained the same:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Separate concerns.&lt;/li&gt;
&lt;li&gt;Keep business logic out of views.&lt;/li&gt;
&lt;li&gt;Make data flow predictable.&lt;/li&gt;
&lt;li&gt;Keep the UI reactive.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A typical modern iOS application looks something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SwiftUI View
      │
      ▼
 ViewModel
      │
      ▼
 Repository
      │
      ▼
API / Database
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The ViewModel receives user intent. The Repository fetches data. The View updates. Everything is deterministic, a button tap follows a predefined execution path.&lt;/p&gt;

&lt;p&gt;This architecture has served us incredibly well. But AI-powered experiences are fundamentally different.&lt;/p&gt;




&lt;h2&gt;
  
  
  Traditional Apps Execute
&lt;/h2&gt;

&lt;p&gt;Traditional applications execute instructions. For example, a user taps:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Search "Blue Jacket"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The flow is straightforward:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;View → ViewModel → Repository → Search API → Products
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every component knows exactly what to do. There is almost no ambiguity.&lt;/p&gt;




&lt;h2&gt;
  
  
  AI Applications Need to Think Before They Act
&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4z2jhiegqj2zux0a0xqq.webp" 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4z2jhiegqj2zux0a0xqq.webp" alt=" " width="800" height="1200"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now consider a modern shopping experience. Instead of typing keywords, the user says:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Find me a blue waterproof jacket under ₹5,000 that's suitable for a rainy trip to Scotland next week."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Notice something interesting: the app cannot immediately call an API. It first needs to understand what the user actually means. It must infer:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Waterproof&lt;/li&gt;
&lt;li&gt;Suitable for cold weather&lt;/li&gt;
&lt;li&gt;Budget constraint&lt;/li&gt;
&lt;li&gt;Destination&lt;/li&gt;
&lt;li&gt;Travel dates&lt;/li&gt;
&lt;li&gt;Product availability&lt;/li&gt;
&lt;li&gt;Ranking preferences&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Before any API is called, reasoning has already begun.&lt;/p&gt;




&lt;h2&gt;
  
  
  One Request Can Become Multiple Tasks
&lt;/h2&gt;

&lt;p&gt;Internally, that single sentence may become:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Understand intent
      ↓
Break problem into tasks
      ↓
Search products
      ↓
Check weather
      ↓
Understand destination climate
      ↓
Filter inventory
      ↓
Rank recommendations
      ↓
Generate explanation
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That isn't something a traditional ViewModel was designed to orchestrate.&lt;/p&gt;




&lt;h2&gt;
  
  
  The ViewModel's Responsibility Hasn't Changed
&lt;/h2&gt;

&lt;p&gt;Many developers immediately ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Isn't an Agent just another ViewModel?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It's a fair question. The answer is &lt;strong&gt;not quite&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A ViewModel's primary responsibility is still:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Manage screen state&lt;/li&gt;
&lt;li&gt;Transform models into UI&lt;/li&gt;
&lt;li&gt;Handle user interactions&lt;/li&gt;
&lt;li&gt;Coordinate presentation logic&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Those responsibilities remain exactly the same even in AI-powered applications. What changes is &lt;strong&gt;who coordinates reasoning&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Enter the Agent
&lt;/h2&gt;

&lt;p&gt;Think of an Agent as an orchestration layer. Instead of simply forwarding requests, it decides &lt;strong&gt;how&lt;/strong&gt; a request should be solved.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User Request
      ↓
   Agent
      ↓
  Reason
      ↓
   Plan
      ↓
Choose tools
      ↓
Execute tools
      ↓
Combine results
      ↓
Return response
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Agent isn't replacing your networking layer. It isn't replacing repositories. It isn't replacing MVVM. &lt;strong&gt;It is coordinating them.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  A Better Mental Model
&lt;/h2&gt;

&lt;p&gt;Instead of thinking:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;View → ViewModel → Agent → Tools → Repositories → Backend → LLM

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frwelkggzykdokt397o7y.webp" 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frwelkggzykdokt397o7y.webp" alt=" " width="800" height="1000"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The ViewModel still owns UI state. The Agent owns intelligence.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Exactly Does an Agent Do?
&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fu7im0iv2wuum9santpf4.webp" 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fu7im0iv2wuum9santpf4.webp" alt=" " width="800" height="1000"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Understand Intent
&lt;/h3&gt;

&lt;p&gt;The user's words are rarely explicit. Example:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Something comfortable for office meetings."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The Agent converts vague language into structured requirements.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Planning
&lt;/h3&gt;

&lt;p&gt;Complex requests are broken into multiple smaller tasks. Instead of:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Call API
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It becomes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Search products → Compare options → Remove unavailable items → Rank → Summarize
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Tool Calling
&lt;/h3&gt;

&lt;p&gt;Modern LLMs don't perform every action themselves instead, they invoke tools. Examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Product Search&lt;/li&gt;
&lt;li&gt;Weather API&lt;/li&gt;
&lt;li&gt;Calendar&lt;/li&gt;
&lt;li&gt;Maps&lt;/li&gt;
&lt;li&gt;Payment&lt;/li&gt;
&lt;li&gt;Database&lt;/li&gt;
&lt;li&gt;Inventory Service&lt;/li&gt;
&lt;li&gt;Recommendation Engine&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Agent decides which tools should be executed and in what order.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Memory
&lt;/h3&gt;

&lt;p&gt;Traditional apps remember UI state. Agents remember conversations. Example:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Show me another one."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Another what? The Agent knows, because it remembers previous context.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Reasoning
&lt;/h3&gt;

&lt;p&gt;Instead of returning raw API results:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Product A
Product B
Product C
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It may produce:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Product B is slightly more expensive, but it offers significantly better waterproofing, making it more suitable for your upcoming trip to Scotland."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's reasoning.&lt;/p&gt;




&lt;h2&gt;
  
  
  Comparing Responsibilities
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Traditional MVVM&lt;/th&gt;
&lt;th&gt;AI-Native Architecture&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;ViewModel manages UI&lt;/td&gt;
&lt;td&gt;ViewModel still manages UI&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Repository fetches data&lt;/td&gt;
&lt;td&gt;Repositories still fetch data&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Business logic&lt;/td&gt;
&lt;td&gt;Business logic still exists&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;API calls&lt;/td&gt;
&lt;td&gt;APIs still exist&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Static flow&lt;/td&gt;
&lt;td&gt;Agent orchestrates dynamic workflows&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Screen state&lt;/td&gt;
&lt;td&gt;Conversation state&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;UI events&lt;/td&gt;
&lt;td&gt;User intent&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Deterministic execution&lt;/td&gt;
&lt;td&gt;Planning and reasoning&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Notice how almost everything remains. We're simply introducing another orchestration layer.&lt;/p&gt;




&lt;h2&gt;
  
  
  Where Should the Agent Live?
&lt;/h2&gt;

&lt;p&gt;This is probably the most interesting architectural discussion. One possible design looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SwiftUI View
      ↓
  ViewModel
      ↓
    Agent
   ├── Memory
   ├── Planner
   ├── Tool Registry
   ├── Prompt Builder
   └── LLM Client
      ↓
 Repositories
      ↓
Backend APIs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This keeps concerns well separated. The ViewModel doesn't suddenly become responsible for prompts, memory, or tool execution.&lt;/p&gt;




&lt;h2&gt;
  
  
  What About Clean Architecture?
&lt;/h2&gt;

&lt;p&gt;If you're using Clean Architecture, the Agent naturally belongs in the domain layer; think of it as another domain service. Your ViewModel simply delegates complex AI tasks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ViewModel → AgentService → Repositories → Tools → LLM
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Repositories continue to own data access. The Agent owns orchestration.&lt;/p&gt;




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

&lt;p&gt;As AI features become more common, we're moving beyond simple request-response interactions. Applications are beginning to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Think&lt;/li&gt;
&lt;li&gt;Plan&lt;/li&gt;
&lt;li&gt;Reason&lt;/li&gt;
&lt;li&gt;Remember&lt;/li&gt;
&lt;li&gt;Decide&lt;/li&gt;
&lt;li&gt;Explain&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That introduces responsibilities that traditional UI architectures were never intended to handle. Trying to push all of that into a ViewModel eventually leads to massive classes with mixed responsibilities. Adding an Agent layer keeps each component focused on what it does best.&lt;/p&gt;




&lt;h2&gt;
  
  
  A Practical Example
&lt;/h2&gt;

&lt;p&gt;Imagine building an AI-powered travel assistant. The user asks:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Plan my weekend trip to Coorg with good vegetarian restaurants, pleasant weather, and hotels under ₹6,000."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;An Agent might:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Understand the destination.&lt;/li&gt;
&lt;li&gt;Fetch weather forecasts.&lt;/li&gt;
&lt;li&gt;Search hotels.&lt;/li&gt;
&lt;li&gt;Search restaurants.&lt;/li&gt;
&lt;li&gt;Rank options.&lt;/li&gt;
&lt;li&gt;Remove unavailable hotels.&lt;/li&gt;
&lt;li&gt;Generate an itinerary.&lt;/li&gt;
&lt;li&gt;Explain why each recommendation was chosen.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Your ViewModel doesn't need to know how those eight steps happen. It simply receives a structured response and updates the UI. That's a much cleaner separation of responsibilities.&lt;/p&gt;




&lt;h2&gt;
  
  
  Looking Ahead
&lt;/h2&gt;

&lt;p&gt;Just as networking logic gradually moved out of ViewControllers and into repositories and services, AI orchestration deserves its own home.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;strong&gt;ViewModel&lt;/strong&gt; remains responsible for presentation.&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;Repository&lt;/strong&gt; remains responsible for data.&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;Backend&lt;/strong&gt; remains responsible for persistence.&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;LLM&lt;/strong&gt; remains responsible for reasoning.&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;Agent&lt;/strong&gt; becomes responsible for coordinating the entire workflow.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Final Thoughts
&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F135ygyhtigt8cx9k4ujc.webp" 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F135ygyhtigt8cx9k4ujc.webp" alt=" " width="800" height="1000"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I don't believe MVVM is becoming obsolete. In fact, it's still one of the best ways to manage presentation logic in SwiftUI.&lt;/p&gt;

&lt;p&gt;What is changing isn't the UI architecture, it's the layer &lt;strong&gt;above&lt;/strong&gt; it.&lt;/p&gt;

&lt;p&gt;The future of intelligent applications isn't about replacing ViewModels. It's about introducing an orchestration layer capable of understanding intent, planning workflows, invoking tools, maintaining context, and delivering intelligent experiences.&lt;/p&gt;

&lt;p&gt;The apps we build tomorrow won't just respond to user actions. They'll collaborate with users to accomplish goals. And that requires more than another ViewModel -&amp;gt; it requires an Agent.&lt;/p&gt;




&lt;h3&gt;
  
  
  What do you think?
&lt;/h3&gt;

&lt;p&gt;If you were designing an AI-first iOS application today:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Would you extend your existing ViewModel?&lt;/li&gt;
&lt;li&gt;Would you introduce a dedicated Agent layer?&lt;/li&gt;
&lt;li&gt;Or do you see another architectural pattern emerging?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I'd love to hear how you're approaching AI-native application architecture.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>mobile</category>
      <category>ios</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Beyond Portrait and Landscape: Building Truly Resizable iOS Apps</title>
      <dc:creator>Vinayak G Hejib</dc:creator>
      <pubDate>Fri, 03 Jul 2026 09:56:33 +0000</pubDate>
      <link>https://dev.to/vnayak_hejib/beyond-portrait-and-landscape-building-truly-resizable-ios-apps-117i</link>
      <guid>https://dev.to/vnayak_hejib/beyond-portrait-and-landscape-building-truly-resizable-ios-apps-117i</guid>
      <description>&lt;h3&gt;
  
  
  &lt;em&gt;Apple: "Your app supports Portrait and Landscape, right?"&lt;/em&gt;
&lt;/h3&gt;

&lt;h3&gt;
  
  
  &lt;em&gt;Also Apple: "Cute. Now make it work at literally any window size."&lt;/em&gt;
&lt;/h3&gt;

&lt;p&gt;_&lt;br&gt;
&lt;em&gt;Why every iOS developer needs to rethink layout architecture in the era of adaptive windows.&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;You've spent months perfecting your iPhone application.&lt;/p&gt;

&lt;p&gt;It looks fantastic on the iPhone SE, the iPhone 16, the Pro Max, and iPad. You ship. Everything works.&lt;/p&gt;

&lt;p&gt;Then Apple pushes the interaction model further freeform resizing, Stage Manager, external displays, floating windows and users start dragging your app's window to whatever size fits their workflow.&lt;/p&gt;

&lt;p&gt;Suddenly, it becomes like in image below:&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F48ov3yi3shfghwll2yp3.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F48ov3yi3shfghwll2yp3.jpeg" alt=" " width="800" height="435"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Nothing technically &lt;em&gt;breaks&lt;/em&gt;. No crash, no constraint warning in the console. But everything &lt;strong&gt;feels&lt;/strong&gt; broken because the layout was designed for &lt;em&gt;screens&lt;/em&gt;, not &lt;em&gt;windows&lt;/em&gt;, and that distinction is about to matter a lot more than it used to.&lt;/p&gt;

&lt;p&gt;For over fifteen years, iOS developers have thought in terms of devices: iPhone, iPad, portrait, landscape. The next era of Apple platforms demands a different mental model one built around &lt;strong&gt;available space&lt;/strong&gt;, not device identity.&lt;/p&gt;

&lt;p&gt;This article walks through that mindset shift and builds a production-ready, adaptive product detail page (PDP) that gracefully reflows as its window changes size including a custom SwiftUI &lt;code&gt;Layout&lt;/code&gt; implementation you can drop straight into a real app.&lt;/p&gt;
&lt;h2&gt;
  
  
  Why This Matters Now
&lt;/h2&gt;

&lt;p&gt;For most of iOS history, your app's canvas was effectively fixed at runtime. Rotation gave you two known layouts, and that was the extent of your "adaptive" surface area.&lt;/p&gt;

&lt;p&gt;That assumption is breaking down. Modern Apple platforms increasingly embrace:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Split View and Slide Over&lt;/strong&gt; on iPad&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stage Manager&lt;/strong&gt;, with genuinely freeform window sizing&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;External and wireless displays&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multiwindow&lt;/strong&gt; scenes on iPadOS and visionOS&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Continuously variable widths&lt;/strong&gt;, not two or three fixed states&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Users don't think about which device they're on. They think about whether your app behaves naturally in whatever space they've given it. Supporting "smaller widths" as an edge case isn't enough anymore the goal is continuous adaptation across the entire width spectrum, not just graceful degradation at the extremes.&lt;/p&gt;
&lt;h2&gt;
  
  
  The Biggest Mistake Most Apps Make
&lt;/h2&gt;

&lt;p&gt;Here's the pattern nearly every app reaches for first:&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;horizontalSizeClass&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;compact&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;ProductMobileView&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;ProductTabletView&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 works until it doesn't. &lt;code&gt;horizontalSizeClass&lt;/code&gt; collapses an enormous range of real widths into exactly two buckets. But picture a window resizing continuously:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;390 → 410 → 455 → 512 → 575 → 612 → 640 → 700 → 790 → 850
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your UI doesn't have two states. It has hundreds. Binary size-class branching leaves a massive, awkward gap between "phone" and "tablet" where the layout is neither optimized nor intentional it's just whichever of the two views got picked, stretched or squeezed to fit.&lt;/p&gt;

&lt;p&gt;The fix isn't a third &lt;code&gt;if&lt;/code&gt; branch. It's abandoning device-based branching as the primary layout mechanism altogether.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Example We'll Build
&lt;/h2&gt;

&lt;p&gt;We'll design a realistic e-commerce Product Detail Page (PDP) the kind you'd find in any modern retail app containing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Gallery&lt;/li&gt;
&lt;li&gt;Product name, brand, rating&lt;/li&gt;
&lt;li&gt;Price&lt;/li&gt;
&lt;li&gt;Colour picker / size picker&lt;/li&gt;
&lt;li&gt;Wishlist action&lt;/li&gt;
&lt;li&gt;Delivery information&lt;/li&gt;
&lt;li&gt;Description&lt;/li&gt;
&lt;li&gt;Reviews&lt;/li&gt;
&lt;li&gt;Recommended products&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This isn't a toy example. It's representative of the layout complexity retail apps deal with daily, where imagery, metadata, actions, and recommendation carousels all have to coexist and reflow together.&lt;/p&gt;

&lt;h2&gt;
  
  
  Starting Point: The Naïve Layout
&lt;/h2&gt;

&lt;p&gt;Most implementations look 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;ScrollView&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="nv"&gt;spacing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;ProductGallery&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="kt"&gt;ProductInformation&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="kt"&gt;SizeSelector&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="kt"&gt;ColorSelector&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="kt"&gt;AddToBagButton&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="kt"&gt;Description&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="kt"&gt;Reviews&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="kt"&gt;Recommendations&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 is fine on a phone. But as available width grows, it becomes wasteful long single-column scrolling, oceans of whitespace on either side, and a gallery that never gets to breathe next to the content it's describing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Thinking in Layout Zones, Not Devices
&lt;/h2&gt;

&lt;p&gt;Instead of asking &lt;em&gt;"Am I on an iPhone?"&lt;/em&gt;, ask &lt;em&gt;"How much horizontal space do I actually have right now?"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This is the fundamental shift. Rather than branching on device identity, define &lt;strong&gt;logical width ranges&lt;/strong&gt; that describe how content should be organized:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Width Range&lt;/th&gt;
&lt;th&gt;Experience&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Narrow&lt;/strong&gt; (&amp;lt; ~600pt)&lt;/td&gt;
&lt;td&gt;Single-column layout optimized for constrained space&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Medium&lt;/strong&gt; (~600–900pt)&lt;/td&gt;
&lt;td&gt;Two-column layout gallery and details side by side&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Wide&lt;/strong&gt; (&amp;gt; ~900pt)&lt;/td&gt;
&lt;td&gt;Multi-pane layout introducing recommendations or supplemental panes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;These are guidelines, not hard device categories the interface adapts continuously as the window changes, and the breakpoints themselves should be tuned against real content, not memorized from a spec sheet.&lt;/p&gt;

&lt;h2&gt;
  
  
  Visual Evolution
&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhngmuy4ucbevy629i0i9.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhngmuy4ucbevy629i0i9.png" alt=" " width="800" height="585"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Notice what &lt;em&gt;doesn't&lt;/em&gt; change: the content. Only its organization shifts. That's the tell of a well-architected adaptive layout the data and view models stay identical; only composition changes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Architecture Before Code
&lt;/h2&gt;

&lt;p&gt;A common anti-pattern is embedding layout decisions directly inside views:&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;width&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;700&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;HStack&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&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;VStack&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&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 works initially but rots fast every screen ends up with its own bespoke breakpoint logic, and nobody remembers why &lt;code&gt;700&lt;/code&gt; was chosen versus &lt;code&gt;680&lt;/code&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2i6hku63fq92jdt1sfjw.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2i6hku63fq92jdt1sfjw.png" alt=" " width="800" height="582"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A more scalable approach separates &lt;strong&gt;layout decision-making&lt;/strong&gt; from &lt;strong&gt;presentation&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;enum&lt;/span&gt; &lt;span class="kt"&gt;LayoutStyle&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;narrow&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;medium&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;wide&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;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;CGFloat&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="n"&gt;width&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;..&amp;lt;&lt;/span&gt;&lt;span class="mi"&gt;600&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="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;narrow&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="mi"&gt;600&lt;/span&gt;&lt;span class="o"&gt;..&amp;lt;&lt;/span&gt;&lt;span class="mi"&gt;900&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="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;medium&lt;/span&gt;
        &lt;span class="k"&gt;default&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="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;wide&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;A single source of truth determines the current &lt;code&gt;LayoutStyle&lt;/code&gt; from available width; the view hierarchy simply reacts to that style. This keeps your UI declarative, testable, and far easier to evolve you can unit test &lt;code&gt;LayoutStyle(width:)&lt;/code&gt; in isolation without spinning up a single view.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reading Width the Right Way: GeometryReader vs. Container Values
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;GeometryReader&lt;/code&gt; is the classic tool here, but it's a blunt instrument it greedily fills its parent and ignores the natural sizing of its children, which can quietly break layouts that worked fine before you added it.&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;AdaptiveProductPage&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;GeometryReader&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;proxy&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;
            &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;style&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;LayoutStyle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;proxy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="kt"&gt;ProductPageContent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;style&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;style&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;For most PDP-style screens this is perfectly adequate, since the page is already the root of its own scroll context. But if you're nesting adaptive components inside other adaptive components, prefer reading size via &lt;code&gt;.onGeometryChange(for:of:action:)&lt;/code&gt; (iOS 18+) or a &lt;code&gt;PreferenceKey&lt;/code&gt;-based width reporter, so children don't accidentally force their parents to expand:&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;WidthReader&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;Content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;View&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&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;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;CGFloat&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;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;CGFloat&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;Content&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="nf"&gt;content&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="p"&gt;)&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="kt"&gt;GeometryReader&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;proxy&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;
                    &lt;span class="kt"&gt;Color&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;clear&lt;/span&gt;
                        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;preference&lt;/span&gt;&lt;span class="p"&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;WidthKey&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="nv"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;proxy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;width&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;onPreferenceChange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;WidthKey&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;width&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$0&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;private&lt;/span&gt; &lt;span class="kd"&gt;struct&lt;/span&gt; &lt;span class="kt"&gt;WidthKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;PreferenceKey&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;var&lt;/span&gt; &lt;span class="nv"&gt;defaultValue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;CGFloat&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;static&lt;/span&gt; &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;inout&lt;/span&gt; &lt;span class="kt"&gt;CGFloat&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;nextValue&lt;/span&gt;&lt;span class="p"&gt;:&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;CGFloat&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;nextValue&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;
  
  
  Composing the Layout
&lt;/h2&gt;

&lt;p&gt;With &lt;code&gt;LayoutStyle&lt;/code&gt; established, the top-level view becomes a simple switch over composition, not appearance:&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;ProductPageContent&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;let&lt;/span&gt; &lt;span class="nv"&gt;style&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;LayoutStyle&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;ScrollView&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="n"&gt;style&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="nv"&gt;narrow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;narrowLayout&lt;/span&gt;
            &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;medium&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;mediumLayout&lt;/span&gt;
            &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;wide&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;wideLayout&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;private&lt;/span&gt; &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;narrowLayout&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="nv"&gt;spacing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kt"&gt;ProductGallery&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="kt"&gt;ProductInformation&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="kt"&gt;SizeSelector&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="kt"&gt;AddToBagButton&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="kt"&gt;Description&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="kt"&gt;Reviews&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="kt"&gt;Recommendations&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;private&lt;/span&gt; &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;mediumLayout&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="nv"&gt;spacing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kt"&gt;HStack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;alignment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;top&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;spacing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="kt"&gt;ProductGallery&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="nv"&gt;alignment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;leading&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;spacing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="kt"&gt;ProductInformation&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                    &lt;span class="kt"&gt;SizeSelector&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                    &lt;span class="kt"&gt;AddToBagButton&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="kt"&gt;Description&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="kt"&gt;Reviews&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;private&lt;/span&gt; &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;wideLayout&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="nv"&gt;spacing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kt"&gt;HStack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;alignment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;top&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;spacing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="kt"&gt;ProductGallery&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;frame&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;maxWidth&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;infinity&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="nv"&gt;alignment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;leading&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;spacing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="kt"&gt;ProductInformation&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                    &lt;span class="kt"&gt;SizeSelector&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                    &lt;span class="kt"&gt;AddToBagButton&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;frame&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;maxWidth&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;340&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="kt"&gt;Recommendations&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;frame&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;maxWidth&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;280&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="kt"&gt;Reviews&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;Each component &lt;code&gt;ProductGallery&lt;/code&gt;, &lt;code&gt;ProductInformation&lt;/code&gt;, &lt;code&gt;SizeSelector&lt;/code&gt;, &lt;code&gt;Recommendations&lt;/code&gt; knows nothing about where it will be placed. It only renders its own content. The composition layer decides arrangement. This separation of concerns means you can reorder, add, or remove regions without touching business logic or view models.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bonus: A Custom &lt;code&gt;Layout&lt;/code&gt; for True Fluidity
&lt;/h2&gt;

&lt;p&gt;Discrete breakpoints (&lt;code&gt;narrow&lt;/code&gt; / &lt;code&gt;medium&lt;/code&gt; / &lt;code&gt;wide&lt;/code&gt;) are a huge improvement over size-class branching, but they're still steps, not a continuum. For cases where you want genuinely fluid reflow say, a tag list or a filter chip row that should wrap based on exact available width SwiftUI's &lt;code&gt;Layout&lt;/code&gt; protocol (iOS 16+) is the right tool:&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;FlowLayout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Layout&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;spacing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;CGFloat&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;

    &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;sizeThatFits&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nv"&gt;proposal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;ProposedViewSize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nv"&gt;subviews&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Subviews&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nv"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;inout&lt;/span&gt; &lt;span class="p"&gt;()&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;CGSize&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;width&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;proposal&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt; &lt;span class="p"&gt;??&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;infinity&lt;/span&gt;
        &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;rowWidth&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;CGFloat&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;totalHeight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;CGFloat&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;rowHeight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;CGFloat&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;for&lt;/span&gt; &lt;span class="n"&gt;subview&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;subviews&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;size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;subview&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sizeThatFits&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;unspecified&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;rowWidth&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;width&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;totalHeight&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;rowHeight&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;spacing&lt;/span&gt;
                &lt;span class="n"&gt;rowWidth&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
                &lt;span class="n"&gt;rowHeight&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="n"&gt;rowWidth&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;spacing&lt;/span&gt;
            &lt;span class="n"&gt;rowHeight&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rowHeight&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;totalHeight&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;rowHeight&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kt"&gt;CGSize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;totalHeight&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;placeSubviews&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nv"&gt;bounds&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;CGRect&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nv"&gt;proposal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;ProposedViewSize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nv"&gt;subviews&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Subviews&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nv"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;inout&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="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bounds&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;minX&lt;/span&gt;
        &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bounds&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;minY&lt;/span&gt;
        &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;rowHeight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;CGFloat&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;for&lt;/span&gt; &lt;span class="n"&gt;subview&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;subviews&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;size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;subview&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sizeThatFits&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;unspecified&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;x&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;bounds&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;maxX&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bounds&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;minX&lt;/span&gt;
                &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;rowHeight&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;spacing&lt;/span&gt;
                &lt;span class="n"&gt;rowHeight&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="n"&gt;subview&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;place&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;at&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;CGPoint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nv"&gt;proposal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;unspecified&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;spacing&lt;/span&gt;
            &lt;span class="n"&gt;rowHeight&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rowHeight&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;height&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;Drop this in for size pickers, filter chips, or delivery-option badges, and they'll wrap naturally at &lt;em&gt;any&lt;/em&gt; width no breakpoint table required. &lt;code&gt;Layout&lt;/code&gt; is also cheap: unlike &lt;code&gt;GeometryReader&lt;/code&gt;, it participates properly in SwiftUI's normal sizing negotiation instead of overriding it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Don't Forget &lt;code&gt;ViewThatFits&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;For simpler cases swapping a compact control for a full one when space runs out &lt;code&gt;ViewThatFits&lt;/code&gt; (iOS 16+) is often all you need, and it's far less code than manual width math:&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;ViewThatFits&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;horizontal&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;HStack&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;"Add to Bag"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="kt"&gt;Image&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;systemName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"bag"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="kt"&gt;Image&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;systemName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"bag"&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;SwiftUI tries each option in order and renders the first one that fits genuinely elegant for toolbar-style controls and buttons that need to shed content before they shed usefulness.&lt;/p&gt;

&lt;h2&gt;
  
  
  Preparing an Existing App
&lt;/h2&gt;

&lt;p&gt;If your app already ships to production, a full rewrite isn't necessary. A gradual migration tends to work best:&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzkg0rawrw75841xwixak.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzkg0rawrw75841xwixak.png" alt=" " width="800" height="433"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Audit&lt;/strong&gt; every place layout branches on device checks or orientation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Replace&lt;/strong&gt; those checks with width-driven &lt;code&gt;LayoutStyle&lt;/code&gt; decisions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Extract&lt;/strong&gt; reusable UI sections (gallery, info block, recommendations) into standalone components with no layout opinions of their own.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Introduce&lt;/strong&gt; an adaptive composition layer that arranges those components differently per &lt;code&gt;LayoutStyle&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test by resizing continuously&lt;/strong&gt; not just by toggling portrait/landscape in the simulator.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This incremental path minimizes risk while steadily raising the ceiling on how well your app handles space it wasn't originally designed for.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing Adaptive Layouts Properly
&lt;/h2&gt;

&lt;p&gt;A layout that "works" only at the exact widths you happened to test isn't adaptive it's coincidentally correct. A few practices that catch real issues early:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Xcode Previews at multiple fixed widths.&lt;/strong&gt; Use &lt;code&gt;.previewLayout(.fixed(width:height:))&lt;/code&gt; across a spread of values (e.g. 320, 428, 600, 834, 1194) rather than relying on device presets alone.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Resize the simulator window live&lt;/strong&gt; rather than only rotating it this is the one Split View and Stage Manager actually exercise.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Snapshot tests at breakpoint boundaries&lt;/strong&gt;, not just breakpoint centers bugs cluster at the transition points (599pt vs. 601pt), not in the middle of a range.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dynamic Type + resizing together.&lt;/strong&gt; A layout that survives resizing but breaks under XXL Dynamic Type isn't done test both axes simultaneously, since real users combine them.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Accessibility Benefits
&lt;/h2&gt;

&lt;p&gt;Designing for resizing pays off beyond windowing scenarios. Users who increase Dynamic Type, enable Display Zoom, or work in Split View are already changing the effective space available to your app from your layout code's perspective, that's indistinguishable from a user resizing a window.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbbmkgz0egjnkj6jzabiz.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbbmkgz0egjnkj6jzabiz.png" alt=" " width="800" height="586"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;An interface built to gracefully reorganize itself across widths is, almost as a side effect, more resilient to larger text, alternative input methods, and whatever form factor Apple ships next. Adaptive design and accessible design reinforce each other; you rarely get one without the other.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;For years, iOS development revolved around screens. We memorized device dimensions, size classes, and orientation pairs.&lt;/p&gt;

&lt;p&gt;The next evolution asks for a different habit of mind not devices, but space; not portrait versus landscape, but fluid layouts that reorganize themselves intelligently as the width beneath them changes.&lt;/p&gt;

&lt;p&gt;Build from adaptable, layout-agnostic components rather than fixed screen assumptions, and your app becomes resilient not just to today's device lineup, but to whatever Apple introduces next.&lt;/p&gt;

&lt;p&gt;The question is no longer &lt;em&gt;"Does my app support iPhone and iPad?"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;It's &lt;em&gt;"Can my interface make the best use of whatever space it's given?"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;That shift is what separates an app that merely runs everywhere from one that actually feels at home everywhere.&lt;/p&gt;

</description>
      <category>ios</category>
      <category>swiftui</category>
      <category>swift</category>
      <category>mobile</category>
    </item>
    <item>
      <title>Building an AI-Powered Git Commit &amp; PR Assistant</title>
      <dc:creator>Vinayak G Hejib</dc:creator>
      <pubDate>Fri, 15 May 2026 12:31:11 +0000</pubDate>
      <link>https://dev.to/vnayak_hejib/building-an-ai-powered-git-commit-pr-assistant-7ma</link>
      <guid>https://dev.to/vnayak_hejib/building-an-ai-powered-git-commit-pr-assistant-7ma</guid>
      <description>&lt;p&gt;Every engineer loves writing code(On a good note, I believe 😉).&lt;/p&gt;

&lt;p&gt;Not every engineer loves writing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;commit messages&lt;/li&gt;
&lt;li&gt;PR descriptions&lt;/li&gt;
&lt;li&gt;testing notes&lt;/li&gt;
&lt;li&gt;release summaries&lt;/li&gt;
&lt;li&gt;sprint updates&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In fast-moving teams, these things usually become an afterthought.&lt;/p&gt;

&lt;p&gt;And honestly, it shows.&lt;/p&gt;

&lt;p&gt;We’ve all seen commits like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;fixed the bug 
changes into MyView file
final-final-fix
ui updates on list view
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or some PR descriptions that simply say:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Please review.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The bigger the codebase gets, the worse this problem becomes.&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%2Fp8ggo88vekao2eq49pmk.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%2Fp8ggo88vekao2eq49pmk.jpeg" alt=" " width="800" height="765"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In modular enterprise applications with multiple teams working in parallel, poor PR communication slows reviews, increases confusion, and creates release risks.&lt;/p&gt;

&lt;p&gt;So I started building something for myself:&lt;/p&gt;

&lt;p&gt;An AI-powered Git assistant that could understand code changes and automatically generate:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;meaningful commit messages&lt;/li&gt;
&lt;li&gt;structured PR summaries&lt;/li&gt;
&lt;li&gt;testing notes&lt;/li&gt;
&lt;li&gt;risk indicators&lt;/li&gt;
&lt;li&gt;release notes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And I wanted it to work &lt;em&gt;offline&lt;/em&gt;.&lt;/p&gt;




&lt;h1&gt;
  
  
  Why I Built It
&lt;/h1&gt;

&lt;p&gt;Initially, this started as a tiny productivity experiment.&lt;/p&gt;

&lt;p&gt;I simply wanted:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;cleaner commit messages&lt;/li&gt;
&lt;li&gt;less repetitive writing&lt;/li&gt;
&lt;li&gt;faster PR creation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But after using it for a few weeks, I realized something interesting:&lt;/p&gt;

&lt;p&gt;The real value wasn’t automation.&lt;/p&gt;

&lt;p&gt;It was reducing &lt;strong&gt;cognitive overhead&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;After spending hours solving architectural or UI problems, context-switching into documentation mode becomes mentally expensive.&lt;/p&gt;

&lt;p&gt;The assistant helped bridge that gap.&lt;/p&gt;




&lt;h1&gt;
  
  
  High-Level Architecture
&lt;/h1&gt;

&lt;p&gt;The workflow is actually pretty straightforward.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Git Diff
   ↓
File Analysis
   ↓
Context Extraction
   ↓
Prompt Generation
   ↓
Local LLM
   ↓
Commit + PR Output
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Fwln3k5cfcj4d9o7sda6g.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%2Fwln3k5cfcj4d9o7sda6g.jpeg" alt=" " width="800" height="772"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The difficult part was making the outputs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;concise&lt;/li&gt;
&lt;li&gt;trustworthy&lt;/li&gt;
&lt;li&gt;reviewer-friendly&lt;/li&gt;
&lt;li&gt;non-robotic&lt;/li&gt;
&lt;/ul&gt;




&lt;h1&gt;
  
  
  Extracting Git Changes
&lt;/h1&gt;

&lt;p&gt;The first version simply passed the raw git diff directly into the model.&lt;/p&gt;

&lt;p&gt;That worked terribly.&lt;/p&gt;

&lt;p&gt;Large diffs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;exceeded context windows&lt;/li&gt;
&lt;li&gt;produced noisy summaries&lt;/li&gt;
&lt;li&gt;generated inaccurate PR descriptions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So I added preprocessing layers.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;subprocess&lt;/span&gt;

&lt;span class="n"&gt;diff&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;check_output&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;git&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;diff&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--cached&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&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;diff&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But raw diffs were too noisy.&lt;/p&gt;

&lt;p&gt;So the assistant started:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;grouping changes by module&lt;/li&gt;
&lt;li&gt;ignoring formatting-only modifications&lt;/li&gt;
&lt;li&gt;detecting API changes&lt;/li&gt;
&lt;li&gt;identifying added vs removed logic&lt;/li&gt;
&lt;/ul&gt;




&lt;h1&gt;
  
  
  Filtering Noise
&lt;/h1&gt;

&lt;p&gt;One surprisingly important improvement was removing low-signal changes.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;should_ignore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;ignored_patterns&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;import &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;swiftlint&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;whitespace&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;any&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;ignored_patterns&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This dramatically improved the quality of generated summaries.&lt;/p&gt;




&lt;h1&gt;
  
  
  Prompt Engineering Was Harder Than Expected
&lt;/h1&gt;

&lt;p&gt;One thing I underestimated was how sensitive outputs were to prompting.&lt;/p&gt;

&lt;p&gt;A vague prompt generated vague PRs.&lt;/p&gt;

&lt;p&gt;An overly detailed prompt generated essays nobody wanted to read.&lt;/p&gt;

&lt;p&gt;Eventually I settled on prompts focused on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;behavioral changes&lt;/li&gt;
&lt;li&gt;architectural impact&lt;/li&gt;
&lt;li&gt;reviewer clarity&lt;/li&gt;
&lt;li&gt;testing implications&lt;/li&gt;
&lt;/ul&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
You are an experienced software engineer reviewing a pull request.

Generate:
1. concise commit message
2. PR summary
3. testing notes
4. possible risks

Ignore formatting-only changes.

Git Diff:
&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;processed_diff&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;
&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The single line:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Ignore formatting-only changes&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;improved results massively.&lt;/p&gt;




&lt;h1&gt;
  
  
  Running AI Offline
&lt;/h1&gt;

&lt;p&gt;This became the most interesting part of the project.&lt;/p&gt;

&lt;p&gt;I specifically wanted:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;local-first workflows&lt;/li&gt;
&lt;li&gt;zero cloud dependency&lt;/li&gt;
&lt;li&gt;privacy for enterprise repositories&lt;/li&gt;
&lt;li&gt;low latency&lt;/li&gt;
&lt;li&gt;no API costs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Sending proprietary diffs to external APIs was something I wanted to avoid entirely.&lt;/p&gt;

&lt;p&gt;So I experimented with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ollama&lt;/li&gt;
&lt;li&gt;llama.cpp&lt;/li&gt;
&lt;li&gt;quantized local models&lt;/li&gt;
&lt;li&gt;Apple Silicon optimizations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Calling the local model was surprisingly simple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;

&lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://localhost:11434/api/generate&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;model&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;mistral&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;prompt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;stream&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;False&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;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;response&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For commit generation and PR summaries, smaller local models were often more than sufficient.&lt;/p&gt;




&lt;h1&gt;
  
  
  The Most Difficult Problems
&lt;/h1&gt;

&lt;p&gt;The difficult part wasn’t generating text.&lt;/p&gt;

&lt;p&gt;It was generating &lt;em&gt;trustworthy&lt;/em&gt; text.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hallucinated Features
&lt;/h2&gt;

&lt;p&gt;Sometimes the model inferred functionality that didn’t exist.&lt;/p&gt;

&lt;p&gt;Especially when refactors looked similar to feature additions.&lt;/p&gt;

&lt;p&gt;To reduce this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;prompts became shorter&lt;/li&gt;
&lt;li&gt;context became more constrained&lt;/li&gt;
&lt;li&gt;diffs were chunked intelligently&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Huge Enterprise Diffs
&lt;/h2&gt;

&lt;p&gt;Large modular repositories create massive PRs.&lt;/p&gt;

&lt;p&gt;Passing entire diffs into the model quickly became inefficient.&lt;/p&gt;

&lt;p&gt;So I added:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;chunking&lt;/li&gt;
&lt;li&gt;module prioritization&lt;/li&gt;
&lt;li&gt;high-signal file detection&lt;/li&gt;
&lt;/ul&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;MAX_CHUNK_SIZE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;4000&lt;/span&gt;

&lt;span class="n"&gt;chunks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="n"&gt;diff&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;MAX_CHUNK_SIZE&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;diff&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;MAX_CHUNK_SIZE&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;
  
  
  Robotic Language
&lt;/h2&gt;

&lt;p&gt;Early outputs sounded overly AI-generated.&lt;/p&gt;

&lt;p&gt;Too many phrases like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;“enhanced functionality”&lt;/li&gt;
&lt;li&gt;“optimized architecture”&lt;/li&gt;
&lt;li&gt;“improved user experience”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Real engineers don’t write like that.&lt;/p&gt;

&lt;p&gt;So prompts were tuned toward:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;concise engineering tone&lt;/li&gt;
&lt;li&gt;direct wording&lt;/li&gt;
&lt;li&gt;reviewer readability&lt;/li&gt;
&lt;/ul&gt;




&lt;h1&gt;
  
  
  Features That Became Surprisingly Useful
&lt;/h1&gt;

&lt;p&gt;The project slowly evolved beyond commit generation.&lt;/p&gt;

&lt;h2&gt;
  
  
  PR Risk Detection
&lt;/h2&gt;

&lt;p&gt;The assistant flags:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;shared module changes&lt;/li&gt;
&lt;li&gt;navigation flow modifications&lt;/li&gt;
&lt;li&gt;authentication updates&lt;/li&gt;
&lt;li&gt;API contract changes&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Automatic Testing Notes
&lt;/h2&gt;

&lt;p&gt;Example output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Tested:
- Login flow
- Session recovery
- Token refresh handling
- Deep link navigation
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This alone ended up saving a surprising amount of time.&lt;/p&gt;




&lt;h2&gt;
  
  
  Release Notes Generation
&lt;/h2&gt;

&lt;p&gt;The assistant can summarize:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;bug fixes&lt;/li&gt;
&lt;li&gt;user-facing improvements&lt;/li&gt;
&lt;li&gt;technical refactors&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;directly from merged commits.&lt;/p&gt;




&lt;h1&gt;
  
  
  Example Output
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Before
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;fixed auth issue
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  After
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Refactor authentication recovery flow to support token refresh handling during session expiration
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h1&gt;
  
  
  What I Learned
&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%2Ftoabcejz2tgc1kj3g7ef.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%2Ftoabcejz2tgc1kj3g7ef.jpeg" alt=" " width="800" height="768"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The biggest realization from this project was:&lt;/p&gt;

&lt;p&gt;AI works best when augmenting engineering workflows, not replacing engineering decisions.&lt;/p&gt;

&lt;p&gt;The assistant is not writing code for me.&lt;/p&gt;

&lt;p&gt;It is removing repetitive cognitive work around:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;communication&lt;/li&gt;
&lt;li&gt;formatting&lt;/li&gt;
&lt;li&gt;summarization&lt;/li&gt;
&lt;li&gt;workflow overhead&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And that turns out to be incredibly valuable.&lt;/p&gt;




&lt;h1&gt;
  
  
  Future Improvements
&lt;/h1&gt;

&lt;p&gt;There’s still a lot I want to explore:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Xcode integration&lt;/li&gt;
&lt;li&gt;Git hook automation&lt;/li&gt;
&lt;li&gt;reviewer suggestions&lt;/li&gt;
&lt;li&gt;Jira linking&lt;/li&gt;
&lt;li&gt;architecture drift detection&lt;/li&gt;
&lt;li&gt;PR quality scoring&lt;/li&gt;
&lt;li&gt;Slack release summaries&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I also want to experiment with embedding-based code understanding for better long-term context awareness.&lt;/p&gt;




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

&lt;p&gt;Building AI tooling for toy projects is easy.&lt;/p&gt;

&lt;p&gt;Building AI tooling that works reliably in large modular enterprise repositories is a completely different challenge.&lt;/p&gt;

&lt;p&gt;But that’s also what makes it exciting.&lt;/p&gt;

&lt;p&gt;What started as a small commit-message helper slowly evolved into a developer workflow copilot that now saves me time almost every day.&lt;/p&gt;

&lt;p&gt;And honestly, this feels like just the beginning of what local AI can do for software engineering workflows.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>productivity</category>
      <category>git</category>
      <category>ios</category>
    </item>
    <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>
  </channel>
</rss>
