<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Keep Writing, Keep Coding</title>
  
  
  <link href="https://ihugo.cc/atom.xml" rel="self"/>
  
  <link href="https://ihugo.cc/"/>
  <updated>2026-05-25T03:24:31.411Z</updated>
  <id>https://ihugo.cc/</id>
  
  <author>
    <name>@ihugo</name>
    
  </author>
  
  <generator uri="https://hexo.io/">Hexo</generator>
  
  <entry>
    <title>Refactoring the Neo4J Cache Layer</title>
    <link href="https://ihugo.cc/2026/04/02/046-008-refactoring-the-neo4j-cache-layer/"/>
    <id>https://ihugo.cc/2026/04/02/046-008-refactoring-the-neo4j-cache-layer/</id>
    <published>2026-04-02T04:31:00.000Z</published>
    <updated>2026-05-25T03:24:31.411Z</updated>
    
    <content type="html"><![CDATA[<p>Last week was almost entirely firefighting. We use Neo4J as our graph query backend, and to speed things up we had been caching a large volume of relationship data in Redis. The problem: the cache kept growing, and so did the AWS bill.</p><p>When I first proposed migrating the cache to DocumentDB (MongoDB-compatible), I estimated it would take a week. In the end, just the initialization alone took two weeks, and the full migration took about three weeks. We hit a lot of unexpected issues along the way. This post documents the approach, the problems, and the outcome.</p><p><img src="/img/046-008-refactoring-the-neo4j-cache-layer.md/8d19a87962ef.png"></p><h2 id="Background-and-Goals"><a href="#Background-and-Goals" class="headerlink" title="Background and Goals"></a>Background and Goals</h2><p>We have a Cache Service that syncs relationship and node data from Neo4J into Redis for fast reads of user relationship data. Performance was never the issue — cost was:</p><ul><li>A large portion of the cached data was rarely accessed, resulting in low cache utilization.</li><li>Memory-optimized instances were holding cold data long-term, which is terrible value for money.</li></ul><p>The goal of this refactor was to introduce a hot&#x2F;cold data separation:</p><ul><li>Hot data stays in Redis.</li><li>Infrequently accessed cache moves to AWS DocumentDB.</li></ul><h2 id="State-of-the-Code-Before"><a href="#State-of-the-Code-Before" class="headerlink" title="State of the Code (Before)"></a>State of the Code (Before)</h2><p>Reading the code before starting, I had a bad feeling.</p><ul><li>Many files across apps&#x2F; shared the same names.</li><li>The code itself was nearly identical across them.</li><li>ElasticSearch logic was mixed in with cache initialization code.</li><li>Incremental cache update logic was scattered across multiple apps.</li><li>Lots of lint errors, lots of commented-out code blocks.</li></ul><p>All classic red flags — any change risked a cascade of side effects, making estimation impossible. Before touching the migration, I did two things first:</p><ol><li>Fixed all related lint issues and deleted dead code.</li><li>Read the entire call chain end-to-end, then cross-checked it against Codex’s analysis.</li></ol><h2 id="Migration-Strategy"><a href="#Migration-Strategy" class="headerlink" title="Migration Strategy"></a>Migration Strategy</h2><p>The approach was low-risk: dual-write first, then shift reads, then shrink Redis.</p><ol><li><strong>Dual-write phase</strong>: writes go to both Redis and DocumentDB simultaneously.</li><li><strong>Validation phase</strong>: monitor consistency and production behavior.</li><li><strong>Convergence phase</strong>: remove unnecessary cold-data writes from Redis.</li></ol><p><img src="/img/046-008-refactoring-the-neo4j-cache-layer.md/1b5056453558.png" alt="1774827639-cache-architecture.png"></p><h2 id="Challenge-1-Query-and-Write-Pressure"><a href="#Challenge-1-Query-and-Write-Pressure" class="headerlink" title="Challenge 1: Query and Write Pressure"></a>Challenge 1: Query and Write Pressure</h2><h3 id="Indexing"><a href="#Indexing" class="headerlink" title="Indexing"></a>Indexing</h3><p>This is a common problem when writing large volumes of data — usually adding an index is enough. However, DocumentDB has poor support for indexing arrays of embedded documents: <code>$in</code> queries on such fields do not use indexes. For example:</p><figure class="shiki json"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"><span style="color: #ABB2BF">{</span></span><span class="line"><span style="color: #ABB2BF">  </span><span style="color: #FFFFFF">...</span></span><span class="line"><span style="color: #ABB2BF">  </span><span style="color: #FFFFFF">identities</span><span style="color: #ABB2BF">: {</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #FFFFFF">platform</span><span style="color: #ABB2BF">: </span><span style="color: #FFFFFF">x</span><span style="color: #ABB2BF">,</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #FFFFFF">id</span><span style="color: #ABB2BF">: </span><span style="color: #D19A66">1234</span><span style="color: #ABB2BF">,</span></span><span class="line"><span style="color: #ABB2BF">  }</span></span><span class="line"><span style="color: #ABB2BF">}</span></span></code></pre></div></div></figure><p>The following query will not hit an index:</p><figure class="shiki js"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"><span style="color: #E5C07B">db</span><span style="color: #ABB2BF">.</span><span style="color: #E5C07B">relation</span><span style="color: #ABB2BF">.</span><span style="color: #61AFEF">find</span><span style="color: #ABB2BF">({</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #E06C75">identities</span><span style="color: #ABB2BF">: {</span></span><span class="line"><span style="color: #ABB2BF">        </span><span style="color: #E06C75">$in</span><span style="color: #ABB2BF">: [</span></span><span class="line"><span style="color: #ABB2BF">            {</span></span><span class="line"><span style="color: #ABB2BF">                </span><span style="color: #E06C75">platform</span><span style="color: #ABB2BF">: </span><span style="color: #98C379">&quot;x&quot;</span><span style="color: #ABB2BF">,</span></span><span class="line"><span style="color: #ABB2BF">                </span><span style="color: #E06C75">id</span><span style="color: #ABB2BF">: </span><span style="color: #D19A66">1234</span><span style="color: #ABB2BF">,</span></span><span class="line"><span style="color: #ABB2BF">            },</span></span><span class="line"><span style="color: #ABB2BF">        ],</span></span><span class="line"><span style="color: #ABB2BF">    },</span></span><span class="line"><span style="color: #ABB2BF">});</span></span></code></pre></div></div></figure><p>To work around this, we manually concatenated the identity fields and stored them as a flat string <code>x_1234</code>. This allows the query to hit the index:</p><figure class="shiki js"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"><span style="color: #E5C07B">db</span><span style="color: #ABB2BF">.</span><span style="color: #E5C07B">relation</span><span style="color: #ABB2BF">.</span><span style="color: #61AFEF">find</span><span style="color: #ABB2BF">({</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #E06C75">identities</span><span style="color: #ABB2BF">: {</span></span><span class="line"><span style="color: #ABB2BF">        </span><span style="color: #E06C75">$in</span><span style="color: #ABB2BF">: [</span><span style="color: #98C379">&quot;x_1234&quot;</span><span style="color: #ABB2BF">],</span></span><span class="line"><span style="color: #ABB2BF">    },</span></span><span class="line"><span style="color: #ABB2BF">});</span></span></code></pre></div></div></figure><h3 id="Database-I-O"><a href="#Database-I-O" class="headerlink" title="Database I&#x2F;O"></a>Database I&#x2F;O</h3><p>The original code was fetching 10,000 records at a time. I had already reduced this to 1,000, but DocumentDB Insights was still flagging many queries with I&#x2F;O warnings. The batch size had to be reduced further to 100 records per query or update.</p><h2 id="Challenge-2-Controlling-Runtime-Memory"><a href="#Challenge-2-Controlling-Runtime-Memory" class="headerlink" title="Challenge 2: Controlling Runtime Memory"></a>Challenge 2: Controlling Runtime Memory</h2><p>The original concurrency was set to 100 with a taskGroup size of 1,000, which meant the EC2 instance had to load 100 × 1,000 records into memory simultaneously — enough to OOM the instance. I went down several dead ends trying to fix this: deduplicating documents in memory, splitting requests into batches of 100. In the end, setting concurrency to 1 and groupSize to 100 solved it completely, making all those earlier optimizations unnecessary — so I deleted that code.</p><h2 id="Challenge-3-Full-Cache-Initialization-Was-Too-Slow"><a href="#Challenge-3-Full-Cache-Initialization-Was-Too-Slow" class="headerlink" title="Challenge 3: Full Cache Initialization Was Too Slow"></a>Challenge 3: Full Cache Initialization Was Too Slow</h2><p>We had long avoided doing a full cache rebuild for exactly this reason: it’s slow. This refactor made it unavoidable, and we needed it to finish fast. Investigating the root cause revealed the main bottleneck: every time a record was written to Redis, it triggered a synchronous HTTP request to update ElasticSearch data and indexes.</p><p>The fix was to decouple ES from the write path entirely. All ES operations were pushed onto an asynq queue, and a dedicated ES-Init Worker was started to process those updates asynchronously.</p><h3 id="AWS-OpenSearch-Default-Request-Body-Limit-1-MB"><a href="#AWS-OpenSearch-Default-Request-Body-Limit-1-MB" class="headerlink" title="AWS OpenSearch Default Request Body Limit: 1 MB"></a>AWS OpenSearch Default Request Body Limit: 1 MB</h3><p>To improve throughput, we batched tasks and submitted data for 1,000 nodes in a single request. This quickly produced a wave of errors. Checking the logs revealed the request body was exceeding the size limit. We extracted the ES code into a shared package at <code>pkg/es</code> and updated the document assembly logic to ensure each request stays under 1 MB.</p><h3 id="Concurrent-ES-Update-Conflicts"><a href="#Concurrent-ES-Update-Conflicts" class="headerlink" title="Concurrent ES Update Conflicts"></a>Concurrent ES Update Conflicts</h3><p>After fixing the request size, things worked locally but still errored in production. The errors turned out to be document version conflicts — the same document was being updated concurrently more than once. Without proper deduplication in place, the only option was to run single-threaded for now. If we want to improve throughput later, we’ll need to deduplicate before dispatching requests — currently the dispatch layer is already working with raw bytes, which makes this tricky. Left as a known issue for future optimization. The priority for now was getting the new cache live.</p><h2 id="Results"><a href="#Results" class="headerlink" title="Results"></a>Results</h2><p>The refactor delivered three concrete wins:</p><ol><li>Redis instance downgraded from <code>cache.r7g.4xlarge</code> to <code>cache.r7g.2xlarge</code>, cutting roughly 60 GB of memory usage and reducing instance cost by about half — saving approximately <strong>$1,000&#x2F;month</strong>.</li><li>Cache utilization improved: DocumentDB now handles infrequently accessed data, Redis is focused on hot data, and the overall cost-efficiency is much better.</li><li>Full cache initialization time cut from ~8 hours to ~4 hours.</li></ol><h2 id="Closing-Thoughts"><a href="#Closing-Thoughts" class="headerlink" title="Closing Thoughts"></a>Closing Thoughts</h2><p>Hold your code to a standard. Don’t copy-paste. Delete dead code instead of commenting it out. Keep things decoupled.</p><p><strong>Most importantly: read the code fully before estimating the work.</strong> Otherwise, be prepared to work late every night for a week.</p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;Last week was almost entirely firefighting. We use Neo4J as our graph query backend, and to speed things up we had been caching a large v</summary>
      
    
    
    
    
    <category term="Postmortem" scheme="https://ihugo.cc/tags/Postmortem/"/>
    
  </entry>
  
  <entry>
    <title>How I Improved CoreText Rendering to 60 FPS</title>
    <link href="https://ihugo.cc/2026/03/02/045-007-how-i-improve-coretext-in-a-scrollview-to-about-60-fps/"/>
    <id>https://ihugo.cc/2026/03/02/045-007-how-i-improve-coretext-in-a-scrollview-to-about-60-fps/</id>
    <published>2026-03-02T05:31:00.000Z</published>
    <updated>2026-05-25T03:24:31.411Z</updated>
    
    <content type="html"><![CDATA[<p><video src="/img/045-007-how-i-improve-coretext-in-a-scrollview-to-about-60-fps.md/b8e3fd3cf029.mp4" preload="metadata" controlslist="nodownload" controls playsinline></video></p><p>Recently, I started building a video player that uses CoreText to render subtitles. However, when scrolling through the UI, the frame rate dropped to an unacceptable 15 FPS. To fix this, I created a demo that wraps a native text view with CoreText rendering, eventually optimizing the performance to a smooth 60 FPS.</p><h2 id="Main-Reasons-for-the-FPS-Drop"><a href="#Main-Reasons-for-the-FPS-Drop" class="headerlink" title="Main Reasons for the FPS Drop"></a>Main Reasons for the FPS Drop</h2><p>I found two main culprits for the performance drop:</p><ol><li><code>intrinsicContentSize</code> was being called too many times.</li><li><code>CTFrameDraw</code> was being called too many times.</li></ol><h3 id="Optimizing-intrinsicContentSize"><a href="#Optimizing-intrinsicContentSize" class="headerlink" title="Optimizing intrinsicContentSize"></a>Optimizing <code>intrinsicContentSize</code></h3><p><code>intrinsicContentSize</code> determines the natural size of a view. A <code>ScrollView</code> needs to fetch this size for all of its child views in order to calculate the layout. Under the hood, this property uses <code>CTFramesetterCreateWithAttributedString</code> and <code>CTFramesetterSuggestFrameSizeWithConstraints</code> to calculate the dimensions based on the text and font size.</p><p>A simple optimization here is to cache the calculated size, using the font size and text content as the cache key. This prevents redundant, expensive calculations.</p><h3 id="Drawing-in-the-Correct-Order"><a href="#Drawing-in-the-Correct-Order" class="headerlink" title="Drawing in the Correct Order"></a>Drawing in the Correct Order</h3><p>In my original code, I was mistakenly calling <code>CTFrameDraw</code> twice—a classic AI-generated mistake! :D</p><figure class="shiki swift"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"><span style="color: #7F848E; font-style: italic">// Code before the fix:</span></span><span class="line"><span style="color: #7F848E; font-style: italic">// ❌ Drawing in the wrong order</span></span><span class="line"><span style="color: #C678DD">override</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">func</span><span style="color: #ABB2BF"> </span><span style="color: #61AFEF">draw</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">_</span><span style="color: #ABB2BF"> </span><span style="color: #ABB2BF; font-style: italic">dirtyRect</span><span style="color: #ABB2BF">: NSRect) {</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> context = NSGraphicsContext.</span><span style="color: #E06C75">current</span><span style="color: #ABB2BF">!.</span><span style="color: #E06C75">cgContext</span></span><span class="line"></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #7F848E; font-style: italic">// Mistake 1: Drawing the text first</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #61AFEF">CTFrameDraw</span><span style="color: #ABB2BF">(frame, context)</span></span><span class="line"></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">if</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> selectionRange = </span><span style="color: #E5C07B">self</span><span style="color: #ABB2BF">.selectionRange {</span></span><span class="line"><span style="color: #ABB2BF">        </span><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> rects = </span><span style="color: #61AFEF">getSelectionRects</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">from</span><span style="color: #ABB2BF">: frame, </span><span style="color: #61AFEF">range</span><span style="color: #ABB2BF">: selectionRange)</span></span><span class="line"></span><span class="line"><span style="color: #ABB2BF">        </span><span style="color: #7F848E; font-style: italic">// Draw the background selection</span></span><span class="line"><span style="color: #ABB2BF">        context.</span><span style="color: #61AFEF">setFillColor</span><span style="color: #ABB2BF">(NSColor.</span><span style="color: #E06C75">selectedTextBackgroundColor</span><span style="color: #ABB2BF">.</span><span style="color: #E06C75">cgColor</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #ABB2BF">        context.</span><span style="color: #61AFEF">fill</span><span style="color: #ABB2BF">(rects)</span></span><span class="line"></span><span class="line"><span style="color: #ABB2BF">        </span><span style="color: #7F848E; font-style: italic">// Mistake 2: The background just hid the text, so we have to draw it again!</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #61AFEF">CTFrameDraw</span><span style="color: #ABB2BF">(frame, context)</span></span></code></pre></div></div></figure><p>Another way to prevent views from being unnecessarily recreated is to assign unique identifiers to your text views using a combination of their text, font size, and color.</p><h2 id="Why-is-Scrolling-Up-Slower-Than-Scrolling-Down"><a href="#Why-is-Scrolling-Up-Slower-Than-Scrolling-Down" class="headerlink" title="Why is Scrolling Up Slower Than Scrolling Down?"></a>Why is Scrolling Up Slower Than Scrolling Down?</h2><p>While investigating these performance issues, I noticed something interesting: scrolling downwards was smooth and stable, but scrolling upwards (from bottom to top) caused severe FPS drops.</p><p>Why does this happen? As mentioned earlier, a <code>ScrollView</code> needs to calculate the layout of its child views. When scrolling up, the system has to calculate the positions of the views <em>above</em> the current viewport. To do this, it needs to figure out the heights of all those preceding views, which repeatedly triggers <code>CTFramesetterSuggestFrameSizeWithConstraints</code>—a highly CPU-intensive operation. This is exactly why caching text sizes, as discussed earlier, is so crucial for performance.</p><h2 id="Core-Code"><a href="#Core-Code" class="headerlink" title="Core Code"></a>Core Code</h2><figure class="shiki swift"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"></span><span class="line"><span style="color: #C678DD">override</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">func</span><span style="color: #ABB2BF"> </span><span style="color: #61AFEF">draw</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">_</span><span style="color: #ABB2BF"> </span><span style="color: #ABB2BF; font-style: italic">dirtyRect</span><span style="color: #ABB2BF">: NSRect) {</span></span><span class="line"><span style="color: #ABB2BF">    NSColor.</span><span style="color: #E06C75">textBackgroundColor</span><span style="color: #ABB2BF">.</span><span style="color: #61AFEF">setFill</span><span style="color: #ABB2BF">()</span></span><span class="line"><span style="color: #ABB2BF">    bounds.</span><span style="color: #61AFEF">fill</span><span style="color: #ABB2BF">()</span></span><span class="line"></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">guard</span><span style="color: #ABB2BF"> !text.isEmpty </span><span style="color: #C678DD">else</span><span style="color: #ABB2BF"> { </span><span style="color: #C678DD">return</span><span style="color: #ABB2BF"> }</span></span><span class="line"></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> insetRect = bounds.</span><span style="color: #61AFEF">insetBy</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">dx</span><span style="color: #ABB2BF">: </span><span style="color: #D19A66">12</span><span style="color: #ABB2BF">, </span><span style="color: #61AFEF">dy</span><span style="color: #ABB2BF">: </span><span style="color: #D19A66">12</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">guard</span><span style="color: #ABB2BF"> insetRect.width &gt; </span><span style="color: #D19A66">1</span><span style="color: #ABB2BF">, insetRect.height &gt; </span><span style="color: #D19A66">1</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">else</span><span style="color: #ABB2BF"> { </span><span style="color: #C678DD">return</span><span style="color: #ABB2BF"> }</span></span><span class="line"></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> frame = </span><span style="color: #61AFEF">frameForCurrentContent</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">in</span><span style="color: #ABB2BF">: insetRect)</span></span><span class="line"></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">guard</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> context = NSGraphicsContext.current?.cgContext </span><span style="color: #C678DD">else</span><span style="color: #ABB2BF"> { </span><span style="color: #C678DD">return</span><span style="color: #ABB2BF"> }</span></span><span class="line"><span style="color: #ABB2BF">    context.</span><span style="color: #61AFEF">saveGState</span><span style="color: #ABB2BF">()</span></span><span class="line"><span style="color: #ABB2BF">    context.</span><span style="color: #E06C75">textMatrix</span><span style="color: #ABB2BF"> = .identity</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #61AFEF">CTFrameDraw</span><span style="color: #ABB2BF">(frame, context)</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #61AFEF">drawSelection</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">in</span><span style="color: #ABB2BF">: frame, </span><span style="color: #61AFEF">context</span><span style="color: #ABB2BF">: context, </span><span style="color: #61AFEF">pathRect</span><span style="color: #ABB2BF">: insetRect)</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #61AFEF">drawComments</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">in</span><span style="color: #ABB2BF">: frame, </span><span style="color: #61AFEF">context</span><span style="color: #ABB2BF">: context, </span><span style="color: #61AFEF">pathRect</span><span style="color: #ABB2BF">: insetRect)</span></span><span class="line"><span style="color: #ABB2BF">    context.</span><span style="color: #61AFEF">restoreGState</span><span style="color: #ABB2BF">()</span></span><span class="line"><span style="color: #ABB2BF">}</span></span><span class="line"></span><span class="line"><span style="color: #C678DD">override</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">var</span><span style="color: #ABB2BF"> intrinsicContentSize: NSSize {</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> contentWidth = </span><span style="color: #56B6C2">max</span><span style="color: #ABB2BF">(</span><span style="color: #D19A66">120</span><span style="color: #ABB2BF">, (bounds.</span><span style="color: #E06C75">width</span><span style="color: #ABB2BF"> &gt; </span><span style="color: #D19A66">1</span><span style="color: #ABB2BF"> ? bounds.</span><span style="color: #E06C75">width</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">:</span><span style="color: #ABB2BF"> </span><span style="color: #D19A66">760</span><span style="color: #ABB2BF">) - </span><span style="color: #D19A66">24</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> height = </span><span style="color: #61AFEF">suggestedTextHeight</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">for</span><span style="color: #ABB2BF">: contentWidth)</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">return</span><span style="color: #ABB2BF"> </span><span style="color: #61AFEF">NSSize</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">width</span><span style="color: #ABB2BF">: NSView.</span><span style="color: #E06C75">noIntrinsicMetric</span><span style="color: #ABB2BF">, </span><span style="color: #61AFEF">height</span><span style="color: #ABB2BF">: </span><span style="color: #56B6C2">max</span><span style="color: #ABB2BF">(</span><span style="color: #D19A66">84</span><span style="color: #ABB2BF">, height + </span><span style="color: #D19A66">24</span><span style="color: #ABB2BF">))</span></span><span class="line"><span style="color: #ABB2BF">}</span></span><span class="line"></span><span class="line"><span style="color: #C678DD">private</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">func</span><span style="color: #ABB2BF"> </span><span style="color: #61AFEF">suggestedTextHeight</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">for</span><span style="color: #ABB2BF"> </span><span style="color: #ABB2BF; font-style: italic">width</span><span style="color: #ABB2BF">: CGFloat) -&gt; CGFloat {</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">guard</span><span style="color: #ABB2BF"> !text.isEmpty </span><span style="color: #C678DD">else</span><span style="color: #ABB2BF"> { </span><span style="color: #C678DD">return</span><span style="color: #ABB2BF"> </span><span style="color: #D19A66">0</span><span style="color: #ABB2BF"> }</span></span><span class="line"></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> key = </span><span style="color: #61AFEF">LayoutCacheKey</span><span style="color: #ABB2BF">(</span></span><span class="line"><span style="color: #ABB2BF">        </span><span style="color: #61AFEF">styleKey</span><span style="color: #ABB2BF">: </span><span style="color: #61AFEF">styleKeyForCurrentContent</span><span style="color: #ABB2BF">(),</span></span><span class="line"><span style="color: #ABB2BF">        </span><span style="color: #61AFEF">contentWidth</span><span style="color: #ABB2BF">: width,</span></span><span class="line"><span style="color: #ABB2BF">        </span><span style="color: #61AFEF">markupVersion</span><span style="color: #ABB2BF">: markupVersion</span></span><span class="line"><span style="color: #ABB2BF">    )</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">if</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> cachedHeightKey,</span></span><span class="line"><span style="color: #ABB2BF">       cachedHeightKey == key,</span></span><span class="line"><span style="color: #ABB2BF">       </span><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> cachedSuggestedHeight {</span></span><span class="line"><span style="color: #ABB2BF">        </span><span style="color: #C678DD">return</span><span style="color: #ABB2BF"> cachedSuggestedHeight</span></span><span class="line"><span style="color: #ABB2BF">    }</span></span><span class="line"></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> attributed = </span><span style="color: #61AFEF">buildAttributedString</span><span style="color: #ABB2BF">()</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> framesetter = </span><span style="color: #61AFEF">CTFramesetterCreateWithAttributedString</span><span style="color: #ABB2BF">(attributed)</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> target = </span><span style="color: #61AFEF">CGSize</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">width</span><span style="color: #ABB2BF">: width, </span><span style="color: #61AFEF">height</span><span style="color: #ABB2BF">: .greatestFiniteMagnitude)</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> size = </span><span style="color: #61AFEF">CTFramesetterSuggestFrameSizeWithConstraints</span><span style="color: #ABB2BF">(</span></span><span class="line"><span style="color: #ABB2BF">        framesetter,</span></span><span class="line"><span style="color: #ABB2BF">        </span><span style="color: #61AFEF">CFRange</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">location</span><span style="color: #ABB2BF">: </span><span style="color: #D19A66">0</span><span style="color: #ABB2BF">, </span><span style="color: #61AFEF">length</span><span style="color: #ABB2BF">: attributed.</span><span style="color: #E06C75">length</span><span style="color: #ABB2BF">),</span></span><span class="line"><span style="color: #ABB2BF">        </span><span style="color: #D19A66">nil</span><span style="color: #ABB2BF">,</span></span><span class="line"><span style="color: #ABB2BF">        target,</span></span><span class="line"><span style="color: #ABB2BF">        </span><span style="color: #D19A66">nil</span></span><span class="line"><span style="color: #ABB2BF">    )</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> measured = </span><span style="color: #61AFEF">ceil</span><span style="color: #ABB2BF">(size.</span><span style="color: #E06C75">height</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #ABB2BF">    cachedHeightKey = key</span></span><span class="line"><span style="color: #ABB2BF">    cachedSuggestedHeight = measured</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">return</span><span style="color: #ABB2BF"> measured</span></span><span class="line"><span style="color: #ABB2BF">}</span></span><span class="line"></span><span class="line"><span style="color: #C678DD">private</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">func</span><span style="color: #ABB2BF"> </span><span style="color: #61AFEF">makeFrame</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">for</span><span style="color: #ABB2BF"> </span><span style="color: #ABB2BF; font-style: italic">string</span><span style="color: #ABB2BF">: NSAttributedString, </span><span style="color: #61AFEF">in</span><span style="color: #ABB2BF"> </span><span style="color: #ABB2BF; font-style: italic">rect</span><span style="color: #ABB2BF">: CGRect) -&gt; CTFrame {</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> framesetter = </span><span style="color: #61AFEF">CTFramesetterCreateWithAttributedString</span><span style="color: #ABB2BF">(string)</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> path = </span><span style="color: #61AFEF">CGPath</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">rect</span><span style="color: #ABB2BF">: rect, </span><span style="color: #61AFEF">transform</span><span style="color: #ABB2BF">: </span><span style="color: #D19A66">nil</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">return</span><span style="color: #ABB2BF"> </span><span style="color: #61AFEF">CTFramesetterCreateFrame</span><span style="color: #ABB2BF">(framesetter, </span><span style="color: #61AFEF">CFRange</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">location</span><span style="color: #ABB2BF">: </span><span style="color: #D19A66">0</span><span style="color: #ABB2BF">, </span><span style="color: #61AFEF">length</span><span style="color: #ABB2BF">: string.</span><span style="color: #E06C75">length</span><span style="color: #ABB2BF">), path, </span><span style="color: #D19A66">nil</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #ABB2BF">}</span></span><span class="line"></span><span class="line"><span style="color: #C678DD">private</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">func</span><span style="color: #ABB2BF"> </span><span style="color: #61AFEF">frameForCurrentContent</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">in</span><span style="color: #ABB2BF"> </span><span style="color: #ABB2BF; font-style: italic">rect</span><span style="color: #ABB2BF">: CGRect) -&gt; CTFrame {</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> styleKey = </span><span style="color: #61AFEF">styleKeyForCurrentContent</span><span style="color: #ABB2BF">()</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> key = </span><span style="color: #61AFEF">LayoutCacheKey</span><span style="color: #ABB2BF">(</span></span><span class="line"><span style="color: #ABB2BF">        </span><span style="color: #61AFEF">styleKey</span><span style="color: #ABB2BF">: styleKey,</span></span><span class="line"><span style="color: #ABB2BF">        </span><span style="color: #61AFEF">contentWidth</span><span style="color: #ABB2BF">: rect.</span><span style="color: #E06C75">width</span><span style="color: #ABB2BF">,</span></span><span class="line"><span style="color: #ABB2BF">        </span><span style="color: #61AFEF">markupVersion</span><span style="color: #ABB2BF">: markupVersion</span></span><span class="line"><span style="color: #ABB2BF">    )</span></span><span class="line"></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">if</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> cachedFrameKey,</span></span><span class="line"><span style="color: #ABB2BF">       cachedFrameKey == key,</span></span><span class="line"><span style="color: #ABB2BF">       </span><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> cachedFrame {</span></span><span class="line"><span style="color: #ABB2BF">        </span><span style="color: #C678DD">return</span><span style="color: #ABB2BF"> cachedFrame</span></span><span class="line"><span style="color: #ABB2BF">    }</span></span><span class="line"></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> attributed = </span><span style="color: #61AFEF">buildAttributedString</span><span style="color: #ABB2BF">()</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> frame = </span><span style="color: #61AFEF">makeFrame</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">for</span><span style="color: #ABB2BF">: attributed, </span><span style="color: #61AFEF">in</span><span style="color: #ABB2BF">: rect)</span></span><span class="line"></span><span class="line"><span style="color: #ABB2BF">    cachedFrameKey = key</span></span><span class="line"><span style="color: #ABB2BF">    cachedFrame = frame</span></span><span class="line"></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">return</span><span style="color: #ABB2BF"> frame</span></span><span class="line"><span style="color: #ABB2BF">}</span></span></code></pre></div></div></figure>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;&lt;video src=&quot;/img/045-007-how-i-improve-coretext-in-a-scrollview-to-about-60-fps.md/b8e3fd3cf029.mp4&quot; preload=&quot;metadata&quot; controlslist=&quot;nod</summary>
      
    
    
    
    <category term="swiftui" scheme="https://ihugo.cc/categories/swiftui/"/>
    
    
    <category term="coretext" scheme="https://ihugo.cc/tags/coretext/"/>
    
    <category term="scrollview" scheme="https://ihugo.cc/tags/scrollview/"/>
    
  </entry>
  
  <entry>
    <title>Swift Concurrency - From GCD to Structured Concurrency</title>
    <link href="https://ihugo.cc/2026/01/31/043-006-swift-concurrency/"/>
    <id>https://ihugo.cc/2026/01/31/043-006-swift-concurrency/</id>
    <published>2026-01-31T05:31:00.000Z</published>
    <updated>2026-05-25T03:24:31.411Z</updated>
    
    <content type="html"><![CDATA[<p><img src="/img/043-006-swift-concurrency.md/5d253f8acc65.png" alt="Swift Concurrency"></p><h1 id="Background"><a href="#Background" class="headerlink" title="Background"></a>Background</h1><p>I remember when I first graduated and started iOS game development, I used <code>NSThread</code> directly for multithreading. When GCD (Grand Central Dispatch) was introduced, it was very convenient to use, and I used it almost exclusively. occasionally, when tasks had dependencies, I would use <code>NSOperation</code>.</p><p>When using multithreading, having too few locks can easily lead to program crashes due to concurrent access to shared data in multiple places; having too many locks can easily cause deadlocks. In short, it was quite a headache. When developing apps, there were a few crashes that were never resolved, and these bugs were very difficult to troubleshoot.</p><p>So Swift introduced the concept of <strong>Structured Concurrency</strong>, whose concurrency threading model ensures no deadlocks. At the same time, the introduction of Actor maximizes data safety. Why maximize? Because Actors have reentrancy issues for performance reasons, and there can also be issues with competitive access to the same data.</p><hr><h1 id="GCD-Grand-Central-Dispatch"><a href="#GCD-Grand-Central-Dispatch" class="headerlink" title="GCD (Grand Central Dispatch)"></a>GCD (Grand Central Dispatch)</h1><p>GCD mainly consists of <strong>queues</strong> and <strong>task closures</strong>. Queues are divided into:</p><ul><li><strong>Serial Queue</strong>: Executes tasks sequentially</li><li><strong>Concurrent Queue</strong>: Executes tasks in parallel</li></ul><p>You will notice there is no concept of threads here. In fact, concurrent queues and threads have a many-to-many relationship:</p><ul><li>A queue can distribute tasks to multiple threads</li><li>A thread can also process tasks distributed by multiple concurrent queues</li></ul><p>GCD encapsulation hides the details of threads. There are some special queues, such as:</p><ul><li><code>main</code> serial queue</li><li><code>global</code> QoS concurrent queue</li></ul><p>You can also define custom queues if necessary.</p><h2 id="Common-GCD-Issues"><a href="#Common-GCD-Issues" class="headerlink" title="Common GCD Issues"></a>Common GCD Issues</h2><p>Synchronously calling tasks in a <strong>Serial Queue can cause program deadlocks</strong>. You need to be careful when using locks to solve data access issues to avoid crashes and deadlocks.</p><p>Although GCD blocks can set priorities, if there is a low-priority task in a Serial queue, and then a high-priority task is added. In this case, since the low-priority task occupies less CPU time, the high-priority task needs to wait for it to complete. Here GCD will perform <strong>Priority Inversion</strong>, adjusting all previous low-priority tasks to be the same as itself. This approach is far less elegant than the structured concurrency model.</p><p>GCD code can easily lead to <strong>Callback Hell</strong>.</p><h2 id="GCD-Group-Usage-Notes"><a href="#GCD-Group-Usage-Notes" class="headerlink" title="GCD Group Usage Notes"></a>GCD Group Usage Notes</h2><p><code>group.enter()</code> &#x2F; <code>group.leave()</code> must appear in <strong>pairs</strong>:</p><figure class="shiki swift"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> group = </span><span style="color: #61AFEF">DispatchGroup</span><span style="color: #ABB2BF">()</span></span><span class="line"><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> queue = DispatchQueue.</span><span style="color: #61AFEF">global</span><span style="color: #ABB2BF">()</span></span><span class="line"></span><span class="line"><span style="color: #7F848E; font-style: italic">// --- Task A ---</span></span><span class="line"><span style="color: #ABB2BF">group.</span><span style="color: #61AFEF">enter</span><span style="color: #ABB2BF">() </span><span style="color: #7F848E; font-style: italic">// 1. Enter group</span></span><span class="line"><span style="color: #ABB2BF">queue.</span><span style="color: #61AFEF">async</span><span style="color: #ABB2BF"> {</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #56B6C2">print</span><span style="color: #ABB2BF">(</span><span style="color: #98C379">&quot;Start requesting data A...&quot;</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #61AFEF">sleep</span><span style="color: #ABB2BF">(</span><span style="color: #D19A66">2</span><span style="color: #ABB2BF">) </span><span style="color: #7F848E; font-style: italic">// Simulate network delay</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #56B6C2">print</span><span style="color: #ABB2BF">(</span><span style="color: #98C379">&quot;Data A request completed&quot;</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #ABB2BF">    group.</span><span style="color: #61AFEF">leave</span><span style="color: #ABB2BF">() </span><span style="color: #7F848E; font-style: italic">// 2. Leave group</span></span><span class="line"><span style="color: #ABB2BF">}</span></span><span class="line"></span><span class="line"><span style="color: #7F848E; font-style: italic">// --- Task B ---</span></span><span class="line"><span style="color: #ABB2BF">group.</span><span style="color: #61AFEF">enter</span><span style="color: #ABB2BF">()</span></span><span class="line"><span style="color: #ABB2BF">queue.</span><span style="color: #61AFEF">async</span><span style="color: #ABB2BF"> {</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #56B6C2">print</span><span style="color: #ABB2BF">(</span><span style="color: #98C379">&quot;Start requesting data B...&quot;</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #61AFEF">sleep</span><span style="color: #ABB2BF">(</span><span style="color: #D19A66">3</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #56B6C2">print</span><span style="color: #ABB2BF">(</span><span style="color: #98C379">&quot;Data B request completed&quot;</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #ABB2BF">    group.</span><span style="color: #61AFEF">leave</span><span style="color: #ABB2BF">()</span></span><span class="line"><span style="color: #ABB2BF">}</span></span><span class="line"></span><span class="line"><span style="color: #7F848E; font-style: italic">// --- Summary Notification ---</span></span><span class="line"><span style="color: #7F848E; font-style: italic">// Note: This will not block the current thread, it is an asynchronous callback</span></span><span class="line"><span style="color: #ABB2BF">group.</span><span style="color: #61AFEF">notify</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">queue</span><span style="color: #ABB2BF">: .</span><span style="color: #E06C75">main</span><span style="color: #ABB2BF">) {</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #56B6C2">print</span><span style="color: #ABB2BF">(</span><span style="color: #98C379">&quot;🎉 All requests completed! Refresh UI&quot;</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #ABB2BF">}</span></span><span class="line"></span><span class="line"><span style="color: #56B6C2">print</span><span style="color: #ABB2BF">(</span><span style="color: #98C379">&quot;I am the main thread, I will not be blocked&quot;</span><span style="color: #ABB2BF">)</span></span></code></pre></div></div></figure><hr><h1 id="NSOperation"><a href="#NSOperation" class="headerlink" title="NSOperation"></a>NSOperation</h1><p>NSOperation is an encapsulation based on GCD. A natural question is: Why do we need NSOperation when we have GCD?</p><h2 id="Advantages-of-NSOperation"><a href="#Advantages-of-NSOperation" class="headerlink" title="Advantages of NSOperation"></a>Advantages of NSOperation</h2><table><thead><tr><th>Feature</th><th>GCD</th><th>NSOperation</th></tr></thead><tbody><tr><td>Concurrency Control</td><td>Hard to control</td><td><code>maxConcurrentOperationCount</code> can easily control concurrency size</td></tr><tr><td>Encapsulation</td><td>Based on C-language Blocks, hard to encapsulate data</td><td>Class, easy to encapsulate and reuse (can be subclassed)</td></tr><tr><td>Dependencies</td><td>Can only synchronize via semaphores, etc.</td><td>Natively supports task dependencies</td></tr><tr><td>Priority Inversion</td><td>Simple adjustment</td><td>Adjusts Priority based on dependencies</td></tr></tbody></table><p>Task organization is less error-prone compared to GCD, and dependencies can be set:</p><figure class="shiki swift"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> queue = </span><span style="color: #61AFEF">OperationQueue</span><span style="color: #ABB2BF">()</span></span><span class="line"></span><span class="line"><span style="color: #7F848E; font-style: italic">// 1. Create specific tasks</span></span><span class="line"><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> opA = </span><span style="color: #61AFEF">BlockOperation</span><span style="color: #ABB2BF"> {</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #56B6C2">print</span><span style="color: #ABB2BF">(</span><span style="color: #98C379">&quot;Task A: Download image&quot;</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #61AFEF">sleep</span><span style="color: #ABB2BF">(</span><span style="color: #D19A66">2</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #ABB2BF">}</span></span><span class="line"></span><span class="line"><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> opB = </span><span style="color: #61AFEF">BlockOperation</span><span style="color: #ABB2BF"> {</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #56B6C2">print</span><span style="color: #ABB2BF">(</span><span style="color: #98C379">&quot;Task B: Download video&quot;</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #61AFEF">sleep</span><span style="color: #ABB2BF">(</span><span style="color: #D19A66">3</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #ABB2BF">}</span></span><span class="line"></span><span class="line"><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> opC = </span><span style="color: #61AFEF">BlockOperation</span><span style="color: #ABB2BF"> {</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #56B6C2">print</span><span style="color: #ABB2BF">(</span><span style="color: #98C379">&quot;Task C: Synthesize and upload to server&quot;</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #ABB2BF">}</span></span><span class="line"></span><span class="line"><span style="color: #7F848E; font-style: italic">// 2. Set dependencies: C depends on A and B completing</span></span><span class="line"><span style="color: #ABB2BF">opC.</span><span style="color: #61AFEF">addDependency</span><span style="color: #ABB2BF">(opA)</span></span><span class="line"><span style="color: #ABB2BF">opC.</span><span style="color: #61AFEF">addDependency</span><span style="color: #ABB2BF">(opB)</span></span><span class="line"></span><span class="line"><span style="color: #7F848E; font-style: italic">// 3. Add to queue</span></span><span class="line"><span style="color: #ABB2BF">queue.</span><span style="color: #61AFEF">addOperations</span><span style="color: #ABB2BF">([opA, opB, opC], </span><span style="color: #61AFEF">waitUntilFinished</span><span style="color: #ABB2BF">: </span><span style="color: #D19A66">false</span><span style="color: #ABB2BF">)</span></span><span class="line"></span><span class="line"><span style="color: #56B6C2">print</span><span style="color: #ABB2BF">(</span><span style="color: #98C379">&quot;Tasks submitted, main thread continues running&quot;</span><span style="color: #ABB2BF">)</span></span></code></pre></div></div></figure><hr><h1 id="Structured-Concurrency"><a href="#Structured-Concurrency" class="headerlink" title="Structured Concurrency"></a>Structured Concurrency</h1><p>From GCD to NSOperation, there has been an improvement in usage safety, but common concurrency programming issues—<strong>data race access</strong> and <strong>deadlocks</strong>—have not been resolved. Additionally, whether using Blocks or Delegates, it is easy to end up with <strong>Callback Hell</strong> and spaghetti code.</p><p>These have all been resolved with the introduction of <code>async/await</code> in Swift.</p><h2 id="What-is-Structured"><a href="#What-is-Structured" class="headerlink" title="What is Structured?"></a>What is Structured?</h2><p>When learning C, many books suggest not using <code>goto</code> syntax because such code is hard to debug and maintain. This means a piece of code has multiple entries and multiple exits, which is <strong>spaghetti code</strong>. Besides <code>goto</code>, closures and delegates also lead to a poor code experience.</p><p>Using <code>async/await</code> can reduce cognitive load, making code easier to maintain.</p><h2 id="Example-of-Callback-Hell"><a href="#Example-of-Callback-Hell" class="headerlink" title="Example of Callback Hell"></a>Example of Callback Hell</h2><figure class="shiki swift"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"><span style="color: #C678DD">func</span><span style="color: #ABB2BF"> </span><span style="color: #61AFEF">loadUserProfileOldSchool</span><span style="color: #ABB2BF">() {</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #56B6C2">print</span><span style="color: #ABB2BF">(</span><span style="color: #98C379">&quot;1. Start login...&quot;</span><span style="color: #ABB2BF">)</span></span><span class="line"></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #7F848E; font-style: italic">// Level 1: Login</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #61AFEF">login</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">username</span><span style="color: #ABB2BF">: </span><span style="color: #98C379">&quot;user&quot;</span><span style="color: #ABB2BF">, </span><span style="color: #61AFEF">password</span><span style="color: #ABB2BF">: </span><span style="color: #98C379">&quot;123&quot;</span><span style="color: #ABB2BF">) { [</span><span style="color: #C678DD">weak</span><span style="color: #ABB2BF"> </span><span style="color: #E5C07B">self</span><span style="color: #ABB2BF">] result </span><span style="color: #C678DD">in</span></span><span class="line"><span style="color: #ABB2BF">        </span><span style="color: #C678DD">guard</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> </span><span style="color: #E5C07B">self</span><span style="color: #ABB2BF"> = </span><span style="color: #E5C07B">self</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">else</span><span style="color: #ABB2BF"> { </span><span style="color: #C678DD">return</span><span style="color: #ABB2BF"> }</span></span><span class="line"></span><span class="line"><span style="color: #ABB2BF">        </span><span style="color: #C678DD">switch</span><span style="color: #ABB2BF"> result {</span></span><span class="line"><span style="color: #ABB2BF">        </span><span style="color: #C678DD">case</span><span style="color: #ABB2BF"> .</span><span style="color: #61AFEF">success</span><span style="color: #ABB2BF">(</span><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> token)</span><span style="color: #C678DD">:</span></span><span class="line"><span style="color: #ABB2BF">            </span><span style="color: #56B6C2">print</span><span style="color: #ABB2BF">(</span><span style="color: #98C379">&quot;2. Login success, Token: </span><span style="color: #C678DD">\(</span><span style="color: #ABB2BF">token</span><span style="color: #C678DD">)</span><span style="color: #98C379">&quot;</span><span style="color: #ABB2BF">)</span></span><span class="line"></span><span class="line"><span style="color: #ABB2BF">            </span><span style="color: #7F848E; font-style: italic">// Level 2: Get ID (Code starts indenting to the right)</span></span><span class="line"><span style="color: #ABB2BF">            </span><span style="color: #E5C07B">self</span><span style="color: #ABB2BF">.</span><span style="color: #61AFEF">fetchUserID</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">token</span><span style="color: #ABB2BF">: token) { [</span><span style="color: #C678DD">weak</span><span style="color: #ABB2BF"> </span><span style="color: #E5C07B">self</span><span style="color: #ABB2BF">] result </span><span style="color: #C678DD">in</span></span><span class="line"><span style="color: #ABB2BF">                </span><span style="color: #C678DD">guard</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> </span><span style="color: #E5C07B">self</span><span style="color: #ABB2BF"> = </span><span style="color: #E5C07B">self</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">else</span><span style="color: #ABB2BF"> { </span><span style="color: #C678DD">return</span><span style="color: #ABB2BF"> }</span></span><span class="line"></span><span class="line"><span style="color: #ABB2BF">                </span><span style="color: #C678DD">switch</span><span style="color: #ABB2BF"> result {</span></span><span class="line"><span style="color: #ABB2BF">                </span><span style="color: #C678DD">case</span><span style="color: #ABB2BF"> .</span><span style="color: #61AFEF">success</span><span style="color: #ABB2BF">(</span><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> userID)</span><span style="color: #C678DD">:</span></span><span class="line"><span style="color: #ABB2BF">                    </span><span style="color: #56B6C2">print</span><span style="color: #ABB2BF">(</span><span style="color: #98C379">&quot;3. Got ID: </span><span style="color: #C678DD">\(</span><span style="color: #ABB2BF">userID</span><span style="color: #C678DD">)</span><span style="color: #98C379">&quot;</span><span style="color: #ABB2BF">)</span></span><span class="line"></span><span class="line"><span style="color: #ABB2BF">                    </span><span style="color: #7F848E; font-style: italic">// Level 3: Get details (Continues indenting right...)</span></span><span class="line"><span style="color: #ABB2BF">                    </span><span style="color: #E5C07B">self</span><span style="color: #ABB2BF">.</span><span style="color: #61AFEF">fetchProfile</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">userID</span><span style="color: #ABB2BF">: userID) { [</span><span style="color: #C678DD">weak</span><span style="color: #ABB2BF"> </span><span style="color: #E5C07B">self</span><span style="color: #ABB2BF">] result </span><span style="color: #C678DD">in</span></span><span class="line"><span style="color: #ABB2BF">                        </span><span style="color: #C678DD">switch</span><span style="color: #ABB2BF"> result {</span></span><span class="line"><span style="color: #ABB2BF">                        </span><span style="color: #C678DD">case</span><span style="color: #ABB2BF"> .</span><span style="color: #61AFEF">success</span><span style="color: #ABB2BF">(</span><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> profile)</span><span style="color: #C678DD">:</span></span><span class="line"><span style="color: #ABB2BF">                            </span><span style="color: #7F848E; font-style: italic">// Finally got the result!</span></span><span class="line"><span style="color: #ABB2BF">                            </span><span style="color: #E5C07B">self</span><span style="color: #ABB2BF">?.</span><span style="color: #61AFEF">updateUI</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">with</span><span style="color: #ABB2BF">: profile)</span></span><span class="line"><span style="color: #ABB2BF">                        </span><span style="color: #C678DD">case</span><span style="color: #ABB2BF"> .</span><span style="color: #61AFEF">failure</span><span style="color: #ABB2BF">(</span><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> error)</span><span style="color: #C678DD">:</span></span><span class="line"><span style="color: #ABB2BF">                            </span><span style="color: #E5C07B">self</span><span style="color: #ABB2BF">?.</span><span style="color: #61AFEF">showError</span><span style="color: #ABB2BF">(error)</span></span><span class="line"><span style="color: #ABB2BF">                        }</span></span><span class="line"><span style="color: #ABB2BF">                    }</span></span><span class="line"></span><span class="line"><span style="color: #ABB2BF">                </span><span style="color: #C678DD">case</span><span style="color: #ABB2BF"> .</span><span style="color: #61AFEF">failure</span><span style="color: #ABB2BF">(</span><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> error)</span><span style="color: #C678DD">:</span></span><span class="line"><span style="color: #ABB2BF">                    </span><span style="color: #7F848E; font-style: italic">// Error handling scattered in each layer</span></span><span class="line"><span style="color: #ABB2BF">                    </span><span style="color: #E5C07B">self</span><span style="color: #ABB2BF">.</span><span style="color: #61AFEF">showError</span><span style="color: #ABB2BF">(error)</span></span><span class="line"><span style="color: #ABB2BF">                }</span></span><span class="line"><span style="color: #ABB2BF">            }</span></span><span class="line"></span><span class="line"><span style="color: #ABB2BF">        </span><span style="color: #C678DD">case</span><span style="color: #ABB2BF"> .</span><span style="color: #61AFEF">failure</span><span style="color: #ABB2BF">(</span><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> error)</span><span style="color: #C678DD">:</span></span><span class="line"><span style="color: #ABB2BF">            </span><span style="color: #E5C07B">self</span><span style="color: #ABB2BF">.</span><span style="color: #61AFEF">showError</span><span style="color: #ABB2BF">(error)</span></span><span class="line"><span style="color: #ABB2BF">        }</span></span><span class="line"><span style="color: #ABB2BF">    }</span></span><span class="line"><span style="color: #ABB2BF">}</span></span></code></pre></div></div></figure><h2 id="Example-of-async-await"><a href="#Example-of-async-await" class="headerlink" title="Example of async&#x2F;await"></a>Example of async&#x2F;await</h2><p>You will find the code is shorter, and most importantly, the code executes <strong>from top to bottom</strong>, making it readable and easy to maintain:</p><figure class="shiki swift"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"><span style="color: #C678DD">func</span><span style="color: #ABB2BF"> </span><span style="color: #61AFEF">loadUserProfileModern</span><span style="color: #ABB2BF">() </span><span style="color: #C678DD">async</span><span style="color: #ABB2BF"> {</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #56B6C2">print</span><span style="color: #ABB2BF">(</span><span style="color: #98C379">&quot;1. Start login...&quot;</span><span style="color: #ABB2BF">)</span></span><span class="line"></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">do</span><span style="color: #ABB2BF"> {</span></span><span class="line"><span style="color: #ABB2BF">        </span><span style="color: #7F848E; font-style: italic">// Code executes sequentially like synchronous code</span></span><span class="line"><span style="color: #ABB2BF">        </span><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> token = </span><span style="color: #C678DD">try</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">await</span><span style="color: #ABB2BF"> </span><span style="color: #61AFEF">login</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">username</span><span style="color: #ABB2BF">: </span><span style="color: #98C379">&quot;user&quot;</span><span style="color: #ABB2BF">, </span><span style="color: #61AFEF">password</span><span style="color: #ABB2BF">: </span><span style="color: #98C379">&quot;123&quot;</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #ABB2BF">        </span><span style="color: #56B6C2">print</span><span style="color: #ABB2BF">(</span><span style="color: #98C379">&quot;2. Login success, Token: </span><span style="color: #C678DD">\(</span><span style="color: #ABB2BF">token</span><span style="color: #C678DD">)</span><span style="color: #98C379">&quot;</span><span style="color: #ABB2BF">)</span></span><span class="line"></span><span class="line"><span style="color: #ABB2BF">        </span><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> userID = </span><span style="color: #C678DD">try</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">await</span><span style="color: #ABB2BF"> </span><span style="color: #61AFEF">fetchUserID</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">token</span><span style="color: #ABB2BF">: token)</span></span><span class="line"><span style="color: #ABB2BF">        </span><span style="color: #56B6C2">print</span><span style="color: #ABB2BF">(</span><span style="color: #98C379">&quot;3. Got ID: </span><span style="color: #C678DD">\(</span><span style="color: #ABB2BF">userID</span><span style="color: #C678DD">)</span><span style="color: #98C379">&quot;</span><span style="color: #ABB2BF">)</span></span><span class="line"></span><span class="line"><span style="color: #ABB2BF">        </span><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> profile = </span><span style="color: #C678DD">try</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">await</span><span style="color: #ABB2BF"> </span><span style="color: #61AFEF">fetchProfile</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">userID</span><span style="color: #ABB2BF">: userID)</span></span><span class="line"></span><span class="line"><span style="color: #ABB2BF">        </span><span style="color: #7F848E; font-style: italic">// Update UI</span></span><span class="line"><span style="color: #ABB2BF">        </span><span style="color: #C678DD">await</span><span style="color: #ABB2BF"> MainActor.</span><span style="color: #61AFEF">run</span><span style="color: #ABB2BF"> {</span></span><span class="line"><span style="color: #ABB2BF">            </span><span style="color: #61AFEF">updateUI</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">with</span><span style="color: #ABB2BF">: profile)</span></span><span class="line"><span style="color: #ABB2BF">        }</span></span><span class="line"><span style="color: #ABB2BF">    } </span><span style="color: #C678DD">catch</span><span style="color: #ABB2BF"> {</span></span><span class="line"><span style="color: #ABB2BF">        </span><span style="color: #7F848E; font-style: italic">// Error handling concentrated in one place!</span></span><span class="line"><span style="color: #ABB2BF">        </span><span style="color: #C678DD">await</span><span style="color: #ABB2BF"> MainActor.</span><span style="color: #61AFEF">run</span><span style="color: #ABB2BF"> {</span></span><span class="line"><span style="color: #ABB2BF">            </span><span style="color: #61AFEF">showError</span><span style="color: #ABB2BF">(error)</span></span><span class="line"><span style="color: #ABB2BF">        }</span></span><span class="line"><span style="color: #ABB2BF">    }</span></span><span class="line"><span style="color: #ABB2BF">}</span></span><span class="line"></span><span class="line"><span style="color: #7F848E; font-style: italic">// How to call</span></span><span class="line"><span style="color: #61AFEF">Task</span><span style="color: #ABB2BF"> {</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">await</span><span style="color: #ABB2BF"> </span><span style="color: #61AFEF">loadUserProfileModern</span><span style="color: #ABB2BF">()</span></span><span class="line"><span style="color: #ABB2BF">}</span></span></code></pre></div></div></figure><hr><h1 id="Concurrency-Threading-Model"><a href="#Concurrency-Threading-Model" class="headerlink" title="Concurrency Threading Model"></a>Concurrency Threading Model</h1><p>When talking about synchronous functions, we think of threads, but when talking about asynchronous functions, we use <strong>Tasks</strong>. Asynchronous functions run within Tasks.</p><p>Swift concurrency introduces a new scheduling method called <strong>Cooperative Thread Pool</strong>, where a serial queue schedules work, abstracting the remaining execution content in a function as a continuation, and then scheduling it. The actual work is handled by a global concurrent queue, which is assigned to threads for execution.</p><p><img src="/img/043-006-swift-concurrency.md/13087e68e078.png" alt="Concurrency Threading Model"></p><p>This new scheduling method ensures the program will not deadlock and also solves the previous Priority Inversion problem.</p><p>When a scheduling thread is idle (e.g., after <code>await</code>), the executor finds the next instruction for it to execute. Unlike traditional preemptive GCD scheduling, this scheduling method uses executors, pending work, and scheduling queues to jointly ensure thread execution instructions.</p><p><img src="/img/043-006-swift-concurrency.md/9bbd779b1f08.png" alt="Thread Scheduling"></p><p>After <code>Task {}</code>, the task is submitted to the Default Executor, then assigned to an idle thread in the thread pool for execution. Upon encountering <code>await</code>, the current task is suspended (context saved to heap, packaged into a Continuation), and other tasks continue execution.</p><p>This is the underlying reason why Swift Concurrency reduces “cognitive load”: You no longer need to worry about “creating too many threads will freeze the App” or “deadlocks”. Because <strong>the number of threads is always equal to the number of CPU cores</strong>, when waiting, everyone yields to each other, rather than hiring temporary workers without limit like GCD.</p><h2 id="Common-Issues"><a href="#Common-Issues" class="headerlink" title="Common Issues"></a>Common Issues</h2><ul><li><strong>Do not block threads</strong>: Although the executor schedules tasks, if all threads are blocked, the program will freeze.</li><li><strong>Try not to use locks</strong>: Even if you must use them, use lock&#x2F;unlock on the same side of an await, otherwise it is easy to cause thread blocking.</li><li><strong>DispatchSemaphore or NSCondition</strong>: Semaphores represented by these will unconditionally block the current thread when waiting, so use them with caution.</li></ul><h2 id="Common-Executors"><a href="#Common-Executors" class="headerlink" title="Common Executors"></a>Common Executors</h2><ul><li>Global Concurrent Executor</li><li>RunLoop Executor</li><li>Schedulable Executor</li><li>Serial Executor</li><li>Task Executor</li></ul><figure class="shiki swift"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> executor = </span><span style="color: #61AFEF">CustomTaskExecutor</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">label</span><span style="color: #ABB2BF">: </span><span style="color: #98C379">&quot;com.example.custom&quot;</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #C678DD">await</span><span style="color: #ABB2BF"> </span><span style="color: #61AFEF">withTaskExecutorPreference</span><span style="color: #ABB2BF">(executor) {</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">await</span><span style="color: #ABB2BF"> </span><span style="color: #61AFEF">performWork</span><span style="color: #ABB2BF">()</span></span><span class="line"><span style="color: #ABB2BF">}</span></span></code></pre></div></div></figure><hr><h1 id="Actor-Model"><a href="#Actor-Model" class="headerlink" title="Actor Model"></a>Actor Model</h1><p>To avoid data races, the Actor model was introduced. <strong>Actor is a reference type</strong>. Each Actor has an Executor to be responsible for external access calls to it.</p><h2 id="Global-Actor"><a href="#Global-Actor" class="headerlink" title="Global Actor"></a>Global Actor</h2><p>Mainly <strong>MainActor</strong>. All code marked with <code>@MainActor</code> belongs to MainActor.</p><hr><h1 id="Sendable"><a href="#Sendable" class="headerlink" title="Sendable"></a>Sendable</h1><p><strong>Sendable</strong> is a marker used to tell the compiler that they can be passed across different concurrency domains.</p><ul><li>Actor is also Sendable</li><li>Basic value types are Sendable</li><li>If a struct only contains basic value types, then it is also Sendable</li><li>If it is a class, all members must be constants, or marked with <code>@unchecked Sendable</code></li></ul><p>Additionally, the <code>sending</code> keyword can transfer ownership and ensure subsequent inability to use. This can also avoid data race analysis for safe passing.</p><hr><h1 id="Summary"><a href="#Summary" class="headerlink" title="Summary"></a>Summary</h1><table><thead><tr><th>Feature</th><th>GCD</th><th>NSOperation</th><th>Swift Concurrency</th></tr></thead><tbody><tr><td>Deadlock Risk</td><td>High</td><td>Medium</td><td>Low (Cooperative scheduling avoids deadlocks)</td></tr><tr><td>Callback Hell</td><td>Easy to fall into</td><td>Easy to fall into</td><td>async&#x2F;await linear code</td></tr><tr><td>Data Safety</td><td>Manual lock management</td><td>Manual lock management</td><td>Actor + Sendable</td></tr><tr><td>Thread Count</td><td>Dynamic (Prone to thread explosion)</td><td>Dynamic (Prone to thread explosion)</td><td>Fixed (CPU core count)</td></tr><tr><td>Priority Inversion</td><td>Risky</td><td>Risky</td><td>Auto-handled</td></tr></tbody></table><p>Swift Concurrency provides a safer and more maintainable concurrency programming solution for iOS&#x2F;macOS development through structured concurrency, the Actor model, and cooperative thread pools. Although the learning curve is steeper, in the long run, it can significantly reduce hard-to-troubleshoot concurrency bugs and improve code quality.</p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;&lt;img src=&quot;/img/043-006-swift-concurrency.md/5d253f8acc65.png&quot; alt=&quot;Swift Concurrency&quot;&gt;&lt;/p&gt;
&lt;h1 id=&quot;Background&quot;&gt;&lt;a href=&quot;#Background&quot; clas</summary>
      
    
    
    
    <category term="swift" scheme="https://ihugo.cc/categories/swift/"/>
    
    
    <category term="swift" scheme="https://ihugo.cc/tags/swift/"/>
    
  </entry>
  
  <entry>
    <title>ios serious 01</title>
    <link href="https://ihugo.cc/2026/01/22/041-004-ios-1/"/>
    <id>https://ihugo.cc/2026/01/22/041-004-ios-1/</id>
    <published>2026-01-22T05:41:00.000Z</published>
    <updated>2026-05-25T03:24:31.411Z</updated>
    
    <content type="html"><![CDATA[<h2 id="01-What’s-the-UIViewController-lifecycle"><a href="#01-What’s-the-UIViewController-lifecycle" class="headerlink" title="01. What’s the UIViewController lifecycle?"></a>01. What’s the <code>UIViewController</code> lifecycle?</h2><ol><li>init</li><li>loadView</li><li>viewDidLoad</li><li>loadViewIfNeeded</li><li>viewWillAppear</li><li>viewWillLayoutSubviews</li><li>viewDidLayoutSubviews</li><li>viewDidAppear</li></ol><span id="more"></span><hr><ol start="9"><li>viewWillDisappear</li><li>viewDidDisappear</li><li>deinit</li></ol><p>I hadn’t noticed <code>loadViewIfNeeded</code> before. Usually I just access <code>vc.view</code> to force the view to load. <code>loadViewIfNeeded</code> is definitely clearer and more explicit—it avoids accidental side-effects when you only want to load the view conditionally.</p><h3 id="viewWillLayoutSubviews"><a href="#viewWillLayoutSubviews" class="headerlink" title="viewWillLayoutSubviews"></a>viewWillLayoutSubviews</h3><p>Use <code>viewWillLayoutSubviews()</code> when you want to update constraints. Examples:</p><ul><li>Activate&#x2F;deactivate constraints depending on size class &#x2F; orientation</li><li>Update constraint constants</li></ul><h3 id="viewDidLayoutSubviews"><a href="#viewDidLayoutSubviews" class="headerlink" title="viewDidLayoutSubviews"></a>viewDidLayoutSubviews</h3><p>Use <code>viewDidLayoutSubviews()</code> when you need the final frames (they are already computed at this point). Examples:</p><ul><li>Setting <code>cornerRadius = view.bounds.height / 2</code></li><li>Updating <code>CAShapeLayer.path</code></li><li>Adjusting <code>contentInset</code> based on final sizes</li><li>Doing “scroll-to-visible” once frames are valid</li></ul><p><img src="/img/041-004-ios-1.md/892ea2027bfb.png" alt="1768975964-vc-lifecycle.png"></p><p>Note: <code>viewDidDisappear()</code> might not be called if a dismissal is cancelled. For example, if you interactively pull down a modal sheet but then release it to cancel the dismissal, the view never actually disappears. These appear&#x2F;disappear methods can be called multiple times during such interactions.</p><h3 id="Rotate-the-screen"><a href="#Rotate-the-screen" class="headerlink" title="Rotate the screen"></a>Rotate the screen</h3><p>Sequence:</p><ul><li>viewWillTransition(to:with:)</li><li>viewWillLayoutSubviews (source VC)</li><li>viewDidLayoutSubviews (source VC)</li><li>viewWillLayoutSubviews (target VC)</li><li>viewDidLayoutSubviews (target VC)</li></ul><h2 id="What’s-the-difference-between-a-struct-and-a-class"><a href="#What’s-the-difference-between-a-struct-and-a-class" class="headerlink" title="What’s the difference between a struct and a class?"></a>What’s the difference between a <code>struct</code> and a class?</h2><p><strong>Struct</strong></p><ul><li>Value type (allocated on stack)</li><li>Thread safe (usually)</li><li>Cannot inherit (but can adopt protocols)</li><li><strong>Default choice</strong></li><li>The Swift standard library uses structs for frequent types: numbers, strings, arrays, and dictionaries.</li><li>Compiler optimization: Copy-on-Write</li></ul><p><strong>Class</strong></p><ul><li>Reference type (allocated on heap)</li><li>Not thread safe</li><li>Can inherit; can adopt protocols</li></ul><h3 id="How-to-choose"><a href="#How-to-choose" class="headerlink" title="How to choose?"></a>How to choose?</h3><ul><li><strong>Struct by default</strong>: Easier for compiler optimization and thread safety.</li><li><strong>Class</strong> if you need interoperability with Objective-C APIs.</li><li><strong>Class</strong> if you need to share mutable state.</li><li><strong>Class</strong> if you need identity (<code>===</code>).</li></ul><h2 id="What’s-protocol-oriented-programming"><a href="#What’s-protocol-oriented-programming" class="headerlink" title="What’s protocol oriented programming?"></a>What’s protocol oriented programming?</h2><p><img src="/img/041-004-ios-1.md/595ef25dde99.png" alt="1769037814-protocols.png"></p><p>Common protocols: <code>Hashable</code>, <code>Decodable</code>, <code>Encodable</code>, <code>Identifiable</code>.</p><ul><li>Define behavior as protocols</li><li>Provide shared implementations via protocol extensions</li><li>Compose types from multiple protocols</li></ul><h3 id="Why"><a href="#Why" class="headerlink" title="Why?"></a>Why?</h3><ul><li>Better reuse than deep inheritance</li><li>More modular and testable</li></ul><h2 id="Concurrency-in-Swift"><a href="#Concurrency-in-Swift" class="headerlink" title="Concurrency in Swift"></a>Concurrency in Swift</h2><h3 id="Grand-Central-Dispatch-GCD"><a href="#Grand-Central-Dispatch-GCD" class="headerlink" title="Grand Central Dispatch (GCD)"></a>Grand Central Dispatch (GCD)</h3><p>Queue types:</p><ul><li>Serial Queue</li><li>Concurrent Queue</li></ul><p>(Another term is “Task” or “Block” for the actual logic code)</p><p><code>DispatchGroup</code>: Manually call <code>enter()</code> and <code>leave()</code> if executing an async task inside the closure.</p><figure class="shiki swift"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> group = </span><span style="color: #61AFEF">DispatchGroup</span><span style="color: #ABB2BF">()</span></span><span class="line"><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> queue = DispatchQueue.</span><span style="color: #61AFEF">global</span><span style="color: #ABB2BF">()</span></span><span class="line"></span><span class="line"><span style="color: #ABB2BF">queue.</span><span style="color: #61AFEF">async</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">group</span><span style="color: #ABB2BF">: group) {</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #7F848E; font-style: italic">// task A</span></span><span class="line"><span style="color: #ABB2BF">}</span></span><span class="line"></span><span class="line"><span style="color: #ABB2BF">queue.</span><span style="color: #61AFEF">async</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">group</span><span style="color: #ABB2BF">: group) {</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #7F848E; font-style: italic">// task B</span></span><span class="line"><span style="color: #ABB2BF">}</span></span><span class="line"></span><span class="line"><span style="color: #ABB2BF">group.</span><span style="color: #61AFEF">notify</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">queue</span><span style="color: #ABB2BF">: .</span><span style="color: #E06C75">main</span><span style="color: #ABB2BF">) {</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #56B6C2">print</span><span style="color: #ABB2BF">(</span><span style="color: #98C379">&quot;done&quot;</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #ABB2BF">}</span></span><span class="line"></span><span class="line"><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> result = group.</span><span style="color: #61AFEF">wait</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">timeout</span><span style="color: #ABB2BF">: .</span><span style="color: #61AFEF">now</span><span style="color: #ABB2BF">() + </span><span style="color: #D19A66">3</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #56B6C2">print</span><span style="color: #ABB2BF">(result == .</span><span style="color: #E06C75">success</span><span style="color: #ABB2BF"> ? </span><span style="color: #98C379">&quot;ok&quot;</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">:</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">&quot;timeout&quot;</span><span style="color: #ABB2BF">)</span></span></code></pre></div></div></figure><p>Delay API:</p><figure class="shiki swift"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"><span style="color: #ABB2BF">DispatchQueue.</span><span style="color: #E06C75">main</span><span style="color: #ABB2BF">.</span><span style="color: #61AFEF">asyncAfter</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">deadline</span><span style="color: #ABB2BF">: .</span><span style="color: #61AFEF">now</span><span style="color: #ABB2BF">() + </span><span style="color: #D19A66">1</span><span style="color: #ABB2BF">) {</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #56B6C2">print</span><span style="color: #ABB2BF">(</span><span style="color: #98C379">&quot;1s later&quot;</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #ABB2BF">}</span></span></code></pre></div></div></figure><p>Note: <code>wait()</code> will block the current thread.</p><p>Cancel an async task:</p><figure class="shiki swift"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"><span style="color: #C678DD">var</span><span style="color: #ABB2BF"> workItem: DispatchWorkItem?</span></span><span class="line"></span><span class="line"><span style="color: #C678DD">func</span><span style="color: #ABB2BF"> </span><span style="color: #61AFEF">schedule</span><span style="color: #ABB2BF">() {</span></span><span class="line"><span style="color: #ABB2BF">    workItem?.</span><span style="color: #61AFEF">cancel</span><span style="color: #ABB2BF">()  </span><span style="color: #7F848E; font-style: italic">// Cancel the previous one</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> item = </span><span style="color: #61AFEF">DispatchWorkItem</span><span style="color: #ABB2BF"> {</span></span><span class="line"><span style="color: #ABB2BF">        </span><span style="color: #56B6C2">print</span><span style="color: #ABB2BF">(</span><span style="color: #98C379">&quot;run&quot;</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #ABB2BF">    }</span></span><span class="line"><span style="color: #ABB2BF">    workItem = item</span></span><span class="line"><span style="color: #ABB2BF">    DispatchQueue.</span><span style="color: #E06C75">main</span><span style="color: #ABB2BF">.</span><span style="color: #61AFEF">asyncAfter</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">deadline</span><span style="color: #ABB2BF">: .</span><span style="color: #61AFEF">now</span><span style="color: #ABB2BF">() + </span><span style="color: #D19A66">0.5</span><span style="color: #ABB2BF">, </span><span style="color: #61AFEF">execute</span><span style="color: #ABB2BF">: item)</span></span><span class="line"><span style="color: #ABB2BF">}</span></span></code></pre></div></div></figure><p>Create a queue:</p><figure class="shiki swift"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> concurrentQueue = </span><span style="color: #61AFEF">DispatchQueue</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">label</span><span style="color: #ABB2BF">: </span><span style="color: #98C379">&quot;com.yourapp.concurrent&quot;</span><span style="color: #ABB2BF">, </span><span style="color: #61AFEF">attributes</span><span style="color: #ABB2BF">: .</span><span style="color: #E06C75">concurrent</span><span style="color: #ABB2BF">)</span></span></code></pre></div></div></figure><h3 id="NSOperation"><a href="#NSOperation" class="headerlink" title="NSOperation"></a>NSOperation</h3><p>Built on top of GCD, but object-oriented.</p><ul><li><strong>Operation</strong>: An abstract class. Use <code>BlockOperation</code> or subclass it.</li><li><strong>OperationQueue</strong>: Executes operations.</li></ul><p><strong>Why use it over GCD?</strong></p><ul><li><strong>Dependencies</strong>: <code>op2.addDependency(op1)</code> (Run op2 after op1).</li><li><strong>Cancelable</strong>: You can cancel operations (need to check <code>isCancelled</code> inside the operation).</li><li><strong>Control</strong>: <code>maxConcurrentOperationCount = 1</code> makes it a serial queue.</li></ul><figure class="shiki swift"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> queue = </span><span style="color: #61AFEF">OperationQueue</span><span style="color: #ABB2BF">()</span></span><span class="line"><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> op1 = </span><span style="color: #61AFEF">BlockOperation</span><span style="color: #ABB2BF"> { </span><span style="color: #56B6C2">print</span><span style="color: #ABB2BF">(</span><span style="color: #98C379">&quot;Task 1&quot;</span><span style="color: #ABB2BF">) }</span></span><span class="line"><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> op2 = </span><span style="color: #61AFEF">BlockOperation</span><span style="color: #ABB2BF"> { </span><span style="color: #56B6C2">print</span><span style="color: #ABB2BF">(</span><span style="color: #98C379">&quot;Task 2&quot;</span><span style="color: #ABB2BF">) }</span></span><span class="line"></span><span class="line"><span style="color: #ABB2BF">op2.</span><span style="color: #61AFEF">addDependency</span><span style="color: #ABB2BF">(op1)</span></span><span class="line"><span style="color: #ABB2BF">queue.</span><span style="color: #61AFEF">addOperations</span><span style="color: #ABB2BF">([op1, op2], </span><span style="color: #61AFEF">waitUntilFinished</span><span style="color: #ABB2BF">: </span><span style="color: #D19A66">false</span><span style="color: #ABB2BF">)</span></span></code></pre></div></div></figure><h3 id="Swift-Concurrency"><a href="#Swift-Concurrency" class="headerlink" title="Swift Concurrency"></a>Swift Concurrency</h3><p>Introduced in Swift 5.5. Replaces closure callback hell.</p><ul><li><strong>async&#x2F;await</strong>: Linear code flow for async operations.</li><li><strong>Task</strong>: The bridge between sync and async worlds. <code>Task { await ... }</code></li><li><strong>async let</strong>: Run tasks in parallel (Structured Concurrency).</li></ul><figure class="shiki swift"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"><span style="color: #7F848E; font-style: italic">// Parallel execution</span></span><span class="line"><span style="color: #C678DD">async</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> image1 = </span><span style="color: #61AFEF">fetchImage</span><span style="color: #ABB2BF">(</span><span style="color: #D19A66">1</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #C678DD">async</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> image2 = </span><span style="color: #61AFEF">fetchImage</span><span style="color: #ABB2BF">(</span><span style="color: #D19A66">2</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> (img1, img2) = </span><span style="color: #C678DD">await</span><span style="color: #ABB2BF"> (image1, image2)</span></span></code></pre></div></div></figure><ul><li><strong>TaskGroup</strong>: Dynamic parallelism (e.g., fetch a list of IDs).</li></ul><figure class="shiki swift"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"><span style="color: #C678DD">await</span><span style="color: #ABB2BF"> </span><span style="color: #61AFEF">withTaskGroup</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">of</span><span style="color: #ABB2BF">: Data.</span><span style="color: #C678DD">self</span><span style="color: #ABB2BF">) { group </span><span style="color: #C678DD">in</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">for</span><span style="color: #ABB2BF"> id </span><span style="color: #C678DD">in</span><span style="color: #ABB2BF"> ids {</span></span><span class="line"><span style="color: #ABB2BF">        group.</span><span style="color: #61AFEF">addTask</span><span style="color: #ABB2BF"> { </span><span style="color: #C678DD">await</span><span style="color: #ABB2BF"> </span><span style="color: #61AFEF">fetch</span><span style="color: #ABB2BF">(id) }</span></span><span class="line"><span style="color: #ABB2BF">    }</span></span><span class="line"><span style="color: #ABB2BF">}</span></span></code></pre></div></div></figure><p><code>of: Data.self</code> acts as a strict contract:</p><ul><li><strong>Constraint</strong>: All child tasks added via <code>group.addTask</code> <strong>must</strong> return <code>Data</code>.</li><li><strong>Type Safety</strong>: If you try to add a task returning <code>Int</code>, the compiler stops you.</li><li><strong>Usage</strong>: When you collect results (<code>for await result in group</code>), Swift knows <code>result</code> is <code>Data</code>.</li></ul><h3 id="Actors-Isolation"><a href="#Actors-Isolation" class="headerlink" title="Actors &amp; Isolation"></a>Actors &amp; Isolation</h3><p><strong>Actor</strong></p><ul><li>Reference type (like class) but thread-safe.</li><li>Protects mutable state. Only one task accesses the mutable state at a time.</li><li>Internal properties are <strong>isolated</strong>. External access requires <code>await</code>.</li></ul><figure class="shiki swift"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"><span style="color: #C678DD">actor</span><span style="color: #ABB2BF"> </span><span style="color: #E5C07B">Counter</span><span style="color: #ABB2BF"> {</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">var</span><span style="color: #ABB2BF"> value = </span><span style="color: #D19A66">0</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">func</span><span style="color: #ABB2BF"> </span><span style="color: #61AFEF">increment</span><span style="color: #ABB2BF">() { value += </span><span style="color: #D19A66">1</span><span style="color: #ABB2BF"> }</span></span><span class="line"><span style="color: #ABB2BF">}</span></span><span class="line"><span style="color: #7F848E; font-style: italic">// Outside: await counter.increment()</span></span></code></pre></div></div></figure><p><strong>MainActor</strong></p><ul><li>A global actor for UI.</li><li><code>@MainActor</code> ensures code runs on the main thread.</li></ul><h2 id="Model-View-Controller"><a href="#Model-View-Controller" class="headerlink" title="Model-View-Controller"></a>Model-View-Controller</h2><p><img src="/img/041-004-ios-1.md/63465731bc88.png" alt="1769046779-mvc.png"></p><figure class="shiki swift"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"><span style="color: #C678DD">weak</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">open</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">var</span><span style="color: #ABB2BF"> dataSource: UITableViewDataSource?</span></span><span class="line"><span style="color: #C678DD">weak</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">open</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">var</span><span style="color: #ABB2BF"> delegate: UITableViewDelegate?</span></span></code></pre></div></div></figure><h2 id="Lazy"><a href="#Lazy" class="headerlink" title="Lazy"></a>Lazy</h2><figure class="shiki swift"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"><span style="color: #C678DD">lazy</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">var</span><span style="color: #ABB2BF"> v: View = {...}()</span></span><span class="line"></span><span class="line"><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> x = numbers.lazy.</span><span style="color: #56B6C2">map</span><span style="color: #ABB2BF"> { </span><span style="color: #E5C07B">$0</span><span style="color: #ABB2BF"> * </span><span style="color: #D19A66">2</span><span style="color: #ABB2BF"> } </span><span style="color: #7F848E; font-style: italic">// x is not [Int]</span></span></code></pre></div></div></figure><p><code>lazy</code> means creation happens only when needed.</p>]]></content>
    
    
    <summary type="html">&lt;h2 id=&quot;01-What’s-the-UIViewController-lifecycle&quot;&gt;&lt;a href=&quot;#01-What’s-the-UIViewController-lifecycle&quot; class=&quot;headerlink&quot; title=&quot;01. What’s the UIViewController lifecycle?&quot;&gt;&lt;/a&gt;01. What’s the &lt;code&gt;UIViewController&lt;/code&gt; lifecycle?&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;init&lt;/li&gt;
&lt;li&gt;loadView&lt;/li&gt;
&lt;li&gt;viewDidLoad&lt;/li&gt;
&lt;li&gt;loadViewIfNeeded&lt;/li&gt;
&lt;li&gt;viewWillAppear&lt;/li&gt;
&lt;li&gt;viewWillLayoutSubviews&lt;/li&gt;
&lt;li&gt;viewDidLayoutSubviews&lt;/li&gt;
&lt;li&gt;viewDidAppear&lt;/li&gt;
&lt;/ol&gt;</summary>
    
    
    
    
    <category term="iOS" scheme="https://ihugo.cc/tags/iOS/"/>
    
  </entry>
  
  <entry>
    <title>My Ideal CI/CD Workflow for Solo macOS Development</title>
    <link href="https://ihugo.cc/2025/12/25/040-my-ideal-ci-cd-workflow-for-solo-macos-development/"/>
    <id>https://ihugo.cc/2025/12/25/040-my-ideal-ci-cd-workflow-for-solo-macos-development/</id>
    <published>2025-12-25T05:41:00.000Z</published>
    <updated>2026-05-25T03:24:31.411Z</updated>
    
    <content type="html"><![CDATA[<p><img src="/img/040-my-ideal-ci-cd-workflow-for-solo-macos-development.md/2756058f272c.png"></p><h2 id="TL-DR"><a href="#TL-DR" class="headerlink" title="TL;DR"></a>TL;DR</h2><p>You can view the full GitHub Workflows file <a href="https://github.com/sunset-valley/ABPlayer/blob/main/.github/workflows/ci.yml">here</a>.</p><p>Recently, I’ve been developing <strong>ABPlayer</strong>, a macOS app that allows users to import audio, supports transcription generation, and enables simultaneous listening and watching. I have configured GitHub Actions into what I consider the optimal setup for a one-person development team.</p><p>I use <strong>Tuist</strong> to generate the macOS project. I chose Tuist because native Xcode project files (<code>.xcodeproj</code>) often suffer from poor readability and prone to conflicts, even in solo projects (and especially in collaboration). With this setup, the CI system only needs to read and process Tuist’s <code>Project.swift</code> file.</p><span id="more"></span><p>The workflow is built on two core principles:</p><ol><li><p><strong>Feature Development:</strong> Develop features on new branches. Every time a PR is submitted, tests run automatically. Once passed, the code is ready to be automatically merged.</p></li><li><p><strong>Automated Releases:</strong> After merging to the <code>main</code> branch, if the app version is higher than the latest version in GitHub Releases, the system automatically builds and publishes it to GitHub Releases.</p></li></ol><h2 id="Job-Tests"><a href="#Job-Tests" class="headerlink" title="Job: Tests"></a>Job: Tests</h2><p>Whenever a new PR is created, the CI automatically builds the project and runs tests. The main steps are:</p><ol><li><p>Checkout code</p></li><li><p>Setup Xcode</p></li><li><p>Setup Swift (Version 6.2)</p></li><li><p>Install <strong>mise</strong> (for installing Tuist)</p></li><li><p>Install Tuist</p></li><li><p>Run tests</p></li></ol><p><img src="/img/040-my-ideal-ci-cd-workflow-for-solo-macos-development.md/69611f6dbd3f.png"></p><p>I use <code>xcodebuild</code> to execute the tests:</p><figure class="shiki yml"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"><span style="color: #ABB2BF">- </span><span style="color: #E06C75">name</span><span style="color: #ABB2BF">: </span><span style="color: #98C379">Run Tests</span></span><span class="line"><span style="color: #ABB2BF">  </span><span style="color: #E06C75">run</span><span style="color: #ABB2BF">: </span><span style="color: #C678DD">|</span></span><span class="line"><span style="color: #98C379">    xcodebuild test \</span></span><span class="line"><span style="color: #98C379">      -workspace ABPlayer.xcworkspace \</span></span><span class="line"><span style="color: #98C379">      -scheme ABPlayer \</span></span><span class="line"><span style="color: #98C379">      -destination &#39;platform=macOS&#39; \</span></span><span class="line"><span style="color: #98C379">      -enableCodeCoverage YES \</span></span><span class="line"><span style="color: #98C379">      -derivedDataPath DerivedData</span></span></code></pre></div></div></figure><h2 id="Job-Build-and-Release"><a href="#Job-Build-and-Release" class="headerlink" title="Job: Build-and-Release"></a>Job: Build-and-Release</h2><p>While this job shares similarities with the test job, there are several key differences:</p><ul><li><p>Comparing the build version against the latest GitHub Release.</p></li><li><p>Building the Artifact and packaging the <code>App.zip</code>.</p></li><li><p>Publishing the latest version to GitHub Releases.</p></li><li><p>Uploading dSYM files to Sentry.</p></li></ul><p><strong>Note on Permissions:</strong> By default, the <code>GITHUB_TOKEN</code> only has read-only access. Publishing an app to Releases requires write permissions. You must explicitly verify <code>contents: write</code> in your YAML file to authorize the CI to publish the release.</p><figure class="shiki yml"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"><span style="color: #E06C75">permissions</span><span style="color: #ABB2BF">:</span></span><span class="line"><span style="color: #ABB2BF">  </span><span style="color: #E06C75">contents</span><span style="color: #ABB2BF">: </span><span style="color: #98C379">write</span></span></code></pre></div></div></figure><h3 id="Version-Comparison"><a href="#Version-Comparison" class="headerlink" title="Version Comparison"></a>Version Comparison</h3><p>The version comparison is primarily implemented via a Shell script.</p><p>First, it uses the GitHub API to fetch the latest version tag from Releases. Then, it uses a script to read the version configured in the Project.swift file. The comparison result is stored in <code>$GITHUB_OUTPUT</code>, allowing subsequent steps to read the steps.compare_versions.outputs.should_release variable directly.</p><figure class="shiki yml"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"><span style="color: #ABB2BF">- </span><span style="color: #E06C75">name</span><span style="color: #ABB2BF">: </span><span style="color: #98C379">Get Current Version</span></span><span class="line"><span style="color: #ABB2BF">  </span><span style="color: #E06C75">id</span><span style="color: #ABB2BF">: </span><span style="color: #98C379">get_version</span></span><span class="line"><span style="color: #ABB2BF">  </span><span style="color: #E06C75">run</span><span style="color: #ABB2BF">: </span><span style="color: #C678DD">|</span></span><span class="line"><span style="color: #98C379">    VERSION=$(grep &#39;&quot;CFBundleShortVersionString&quot;:&#39; Project.swift | sed -E &#39;s/.*&quot;CFBundleShortVersionString&quot;: &quot;([^&quot;]+)&quot;.*/\1/&#39;)</span></span><span class="line"><span style="color: #98C379">    echo &quot;version=$VERSION&quot; &gt;&gt; $GITHUB_OUTPUT</span></span><span class="line"><span style="color: #98C379">    echo &quot;Current Version: $VERSION&quot;</span></span><span class="line"></span><span class="line"><span style="color: #ABB2BF">- </span><span style="color: #E06C75">name</span><span style="color: #ABB2BF">: </span><span style="color: #98C379">Get Latest Release Version</span></span><span class="line"><span style="color: #ABB2BF">  </span><span style="color: #E06C75">id</span><span style="color: #ABB2BF">: </span><span style="color: #98C379">get_latest_release</span></span><span class="line"><span style="color: #ABB2BF">  </span><span style="color: #E06C75">env</span><span style="color: #ABB2BF">:</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #E06C75">GH_TOKEN</span><span style="color: #ABB2BF">: </span><span style="color: #98C379">${{ secrets.GITHUB_TOKEN }}</span></span><span class="line"><span style="color: #ABB2BF">  </span><span style="color: #E06C75">run</span><span style="color: #ABB2BF">: </span><span style="color: #C678DD">|</span></span><span class="line"><span style="color: #98C379">    LATEST_TAG=$(gh release view --json tagName --jq .tagName 2&gt;/dev/null || echo &quot;0.0.0&quot;)</span></span><span class="line"><span style="color: #98C379">    echo &quot;latest_tag=$LATEST_TAG&quot; &gt;&gt; $GITHUB_OUTPUT</span></span><span class="line"><span style="color: #98C379">    echo &quot;Latest Release Tag: $LATEST_TAG&quot;</span></span><span class="line"></span><span class="line"><span style="color: #ABB2BF">- </span><span style="color: #E06C75">name</span><span style="color: #ABB2BF">: </span><span style="color: #98C379">Compare Versions</span></span><span class="line"><span style="color: #ABB2BF">  </span><span style="color: #E06C75">id</span><span style="color: #ABB2BF">: </span><span style="color: #98C379">compare_versions</span></span><span class="line"><span style="color: #ABB2BF">  </span><span style="color: #E06C75">run</span><span style="color: #ABB2BF">: </span><span style="color: #C678DD">|</span></span><span class="line"><span style="color: #98C379">    CURRENT=&quot;${{ steps.get_version.outputs.version }}&quot;</span></span><span class="line"><span style="color: #98C379">    LATEST=&quot;${{ steps.get_latest_release.outputs.latest_tag }}&quot;</span></span><span class="line"><span style="color: #98C379">    </span></span><span class="line"><span style="color: #98C379">    # Remove &#39;v&#39; prefix if exists for comparison</span></span><span class="line"><span style="color: #98C379">    CURRENT_CLEAN=$(echo $CURRENT | sed &#39;s/^v//&#39;)</span></span><span class="line"><span style="color: #98C379">    LATEST_CLEAN=$(echo $LATEST | sed &#39;s/^v//&#39;)</span></span><span class="line"><span style="color: #98C379">    </span></span><span class="line"><span style="color: #98C379">    if [ &quot;$CURRENT_CLEAN&quot; = &quot;$LATEST_CLEAN&quot; ]; then</span></span><span class="line"><span style="color: #98C379">      echo &quot;Versions are the same. Skipping release.&quot;</span></span><span class="line"><span style="color: #98C379">      echo &quot;should_release=false&quot; &gt;&gt; $GITHUB_OUTPUT</span></span><span class="line"><span style="color: #98C379">    else</span></span><span class="line"><span style="color: #98C379">      HIGHER=$(printf &quot;$CURRENT_CLEAN\n$LATEST_CLEAN&quot; | sort -V | tail -n1)</span></span><span class="line"><span style="color: #98C379">      if [ &quot;$HIGHER&quot; = &quot;$CURRENT_CLEAN&quot; ] &amp;&amp; [ &quot;$CURRENT_CLEAN&quot; != &quot;$LATEST_CLEAN&quot; ]; then</span></span><span class="line"><span style="color: #98C379">        echo &quot;Current version $CURRENT is higher than latest $LATEST. Proceeding with release.&quot;</span></span><span class="line"><span style="color: #98C379">        echo &quot;should_release=true&quot; &gt;&gt; $GITHUB_OUTPUT</span></span><span class="line"><span style="color: #98C379">      else</span></span><span class="line"><span style="color: #98C379">        echo &quot;Current version $CURRENT is NOT higher than latest $LATEST. Skipping release.&quot;</span></span><span class="line"><span style="color: #98C379">        echo &quot;should_release=false&quot; &gt;&gt; $GITHUB_OUTPUT</span></span><span class="line"><span style="color: #98C379">      fi</span></span><span class="line"><span style="color: #98C379">    fi</span></span></code></pre></div></div></figure><h2 id="Other-Essential-Configurations"><a href="#Other-Essential-Configurations" class="headerlink" title="Other Essential Configurations"></a>Other Essential Configurations</h2><p><img src="/img/040-my-ideal-ci-cd-workflow-for-solo-macos-development.md/4b0743fbe785.png"></p><p>Humans are prone to laziness, but machines are not. Therefore, I enforce a rule that code must pass tests before it can be merged into <code>main</code>. This is absolutely necessary.</p><p>By configuring <strong>Branch Protection Rules</strong>, you can enforce this requirement. For the <code>main</code> branch, you need to check <code>Require status checks to pass</code>. In addition to this, I have configured the following:</p><ul><li><p>Restrict deletions</p></li><li><p>Require a pull request before merging</p></li><li><p>Require status checks to pass</p></li><li><p>Block force pushes</p></li></ul><h2 id="Future-Ideas"><a href="#Future-Ideas" class="headerlink" title="Future Ideas"></a>Future Ideas</h2><p>I plan to automate the changelog generation by reading the current version’s changes—including bug fixes and new requirements—from a <code>CHANGELOG</code> file and writing these updates directly into the GitHub Releases.</p><hr><p><em>If your team is hiring for iOS &#x2F; Swift &#x2F; SwiftUI roles (Calgary or remote), I’d love to connect.</em></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;&lt;img src=&quot;/img/040-my-ideal-ci-cd-workflow-for-solo-macos-development.md/2756058f272c.png&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;TL-DR&quot;&gt;&lt;a href=&quot;#TL-DR&quot; class=&quot;headerlink&quot; title=&quot;TL;DR&quot;&gt;&lt;/a&gt;TL;DR&lt;/h2&gt;&lt;p&gt;You can view the full GitHub Workflows file &lt;a href=&quot;https://github.com/sunset-valley/ABPlayer/blob/main/.github/workflows/ci.yml&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Recently, I’ve been developing &lt;strong&gt;ABPlayer&lt;/strong&gt;, a macOS app that allows users to import audio, supports transcription generation, and enables simultaneous listening and watching. I have configured GitHub Actions into what I consider the optimal setup for a one-person development team.&lt;/p&gt;
&lt;p&gt;I use &lt;strong&gt;Tuist&lt;/strong&gt; to generate the macOS project. I chose Tuist because native Xcode project files (&lt;code&gt;.xcodeproj&lt;/code&gt;) often suffer from poor readability and prone to conflicts, even in solo projects (and especially in collaboration). With this setup, the CI system only needs to read and process Tuist’s &lt;code&gt;Project.swift&lt;/code&gt; file.&lt;/p&gt;</summary>
    
    
    
    <category term="Dev" scheme="https://ihugo.cc/categories/Dev/"/>
    
    
    <category term="Engineering" scheme="https://ihugo.cc/tags/Engineering/"/>
    
  </entry>
  
  <entry>
    <title>Using Nano Banana Pro to create an app icon</title>
    <link href="https://ihugo.cc/2025/12/08/038-using-nano-banana-pro-to-create-an-app-icon/"/>
    <id>https://ihugo.cc/2025/12/08/038-using-nano-banana-pro-to-create-an-app-icon/</id>
    <published>2025-12-08T09:43:00.000Z</published>
    <updated>2026-05-25T03:24:31.411Z</updated>
    
    <content type="html"><![CDATA[<p><img src="/img/038-using-nano-banana-pro-to-create-an-app-icon.md/68cdfcac4be6.png"></p><figure class="shiki bash"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"><span style="color: #61AFEF">A</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">minimalist</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">abstract</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">graphic</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">design</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">of</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">a</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">sunrise</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">breaking</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">through</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">darkness.</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">The</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">composition</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">is</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">a</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">full</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">square,</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">edge-to-edge</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">design</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">with</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">no</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">borders</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">and</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">no</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">rounded</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">corners.</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">The</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">bottom</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">section</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">is</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">deep</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">midnight</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">black</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">representing</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">the</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">night.</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">A</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">warm,</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">glowing</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">amber</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">and</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">golden</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">apricot</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">light</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">rises</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">softly</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">from</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">the</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">horizon</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">line,</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">diffusing</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">into</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">the</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">darkness.</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">Atmospheric</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">lighting,</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">subtle</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">grain</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">texture,</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">smooth</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">gradients,</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">modern</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">aesthetic.</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">High</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">definition,</span><span style="color: #ABB2BF"> </span><span style="color: #D19A66">8</span><span style="color: #98C379">k</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">resolution,</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">flat</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">surface.</span><span style="color: #ABB2BF"> </span><span style="color: #D19A66">--ar</span><span style="color: #ABB2BF"> </span><span style="color: #D19A66">1</span><span style="color: #98C379">:1</span></span></code></pre></div></div></figure><p>This is the App icon generated using the prompt above.</p><h2 id="How-to-Generate-the-Prompt-Above"><a href="#How-to-Generate-the-Prompt-Above" class="headerlink" title="How to Generate the Prompt Above"></a>How to Generate the Prompt Above</h2><p>Use the prompt below to create the actual prompt for the App Icon. I used Chinese for this example, but feel free to translate it into English and tweak it to fit your needs. I’m just sharing the general workflow here.</p><figure class="shiki bash"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"><span style="color: #61AFEF">需要生成一个iOS</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">app</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">的icon,</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">辅助我写prompt.</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">你是一个位资深的app设计师.</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">对于绘制app</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">icon非常精通,</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">能用流程化的方式,</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">绘制出简洁又有深意的App</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">icon</span></span></code></pre></div></div></figure><p>I sent this to Gemini 3 Pro and got the following response:</p><figure class="shiki bash"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"><span style="color: #61AFEF">你好。很高兴能以资深设计师的身份协助你。</span></span><span class="line"></span><span class="line"><span style="color: #61AFEF">设计一枚优秀的</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">iOS</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">App</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">Icon，绝不仅仅是画一个好看的图形。它是在方寸之间，用最极简的视觉语言，浓缩产品的核心价值和灵魂。它必须在拥挤的手机主屏幕上瞬间抓住用户的眼球，并传达出“我是干什么的”。</span></span><span class="line"></span><span class="line"><span style="color: #61AFEF">作为一名深耕</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">iOS</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">设计规范多年的设计师，我有一套成熟的“结构化设计流程”。我们将分四个阶段来完成这个任务。请跟随我的引导，一步步提供必要的信息，最后我会为你合成一条高质量的</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">AI</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">绘图</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">Prompt。</span></span><span class="line"></span><span class="line"><span style="color: #61AFEF">我们的设计流程：</span></span><span class="line"><span style="color: #61AFEF">核心定义</span><span style="color: #ABB2BF"> (Discovery): 理解 App 的灵魂。</span></span><span class="line"></span><span class="line"><span style="color: #61AFEF">隐喻与符号</span><span style="color: #ABB2BF"> (Metaphor): 寻找视觉载体。</span></span><span class="line"></span><span class="line"><span style="color: #61AFEF">风格与美学</span><span style="color: #ABB2BF"> (Aesthetics): 确定视觉语言。</span></span><span class="line"></span><span class="line"><span style="color: #61AFEF">Prompt</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">合成</span><span style="color: #ABB2BF"> (Synthesis): 生成最终指令。</span></span><span class="line"></span><span class="line"><span style="color: #61AFEF">阶段一：核心定义</span><span style="color: #ABB2BF"> (Discovery)</span></span><span class="line"><span style="color: #61AFEF">一枚好的图标始于对产品的深刻理解。请尽可能精炼地回答以下问题：</span></span><span class="line"></span><span class="line"><span style="color: #61AFEF">App</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">的核心功能是什么？</span><span style="color: #ABB2BF"> (请用一句话概括，例如：“一个帮助用户管理植物浇水周期的工具” </span><span style="color: #98C379">或</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">“一个专注于极简主义的番茄工作法时钟”。</span><span style="color: #ABB2BF">)</span></span><span class="line"></span><span class="line"><span style="color: #61AFEF">App</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">的目标用户和情绪基调</span><span style="color: #ABB2BF"> (Vibe) 是怎样的？ (</span><span style="color: #61AFEF">例如：专业的/金融的；活泼的/游戏化的；宁静的/冥想的；高效的/工具类的。</span><span style="color: #ABB2BF">)</span></span><span class="line"></span><span class="line"><span style="color: #61AFEF">App</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">的名称是什么？</span><span style="color: #ABB2BF"> (有时名称本身就包含视觉线索，比如 </span><span style="color: #98C379">&quot;Spotify&quot;</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">或</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">&quot;Airbnb&quot;。如果没有也没关系，切记</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">iOS</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">图标中尽量不要出现文字。</span><span style="color: #ABB2BF">)</span></span><span class="line"></span><span class="line"><span style="color: #61AFEF">👉</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">请在此处回复阶段一的答案。</span></span><span class="line"></span><span class="line"><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">等待你的回复后，我会引导你进入下一阶段。为了演示，我先假设一个案例，展示整个流程是如何运作的</span><span style="color: #ABB2BF">)</span></span><span class="line"></span><span class="line"><span style="color: #61AFEF">【演示案例：假设你要做一个名为</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">&quot;FlowWrite&quot;</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">的极简写作</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">App】</span></span><span class="line"><span style="color: #61AFEF">假设你给我的回复是：</span></span><span class="line"></span><span class="line"><span style="color: #61AFEF">核心功能：一个让用户专注于写作，排除一切干扰的极简文本编辑器。</span></span><span class="line"></span><span class="line"><span style="color: #61AFEF">基调：宁静的、富有创造力的、极其干净和现代的。</span></span><span class="line"></span><span class="line"><span style="color: #61AFEF">名称：FlowWrite。</span></span><span class="line"></span><span class="line"><span style="color: #61AFEF">阶段二：隐喻与符号</span><span style="color: #ABB2BF"> (Metaphor)</span></span><span class="line"><span style="color: #61AFEF">（根据你的回复，我会分析并提出建议。以下是基于演示案例的分析）</span></span><span class="line"></span><span class="line"><span style="color: #61AFEF">设计师分析：</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">对于“专注写作”和“心流</span><span style="color: #ABB2BF"> (Flow)”，我们不能只画一支笔或一张纸，那样太直白且无聊。我们需要寻找更深层次的隐喻。</span></span><span class="line"></span><span class="line"><span style="color: #61AFEF">概念</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">A</span><span style="color: #ABB2BF"> (侧重“心流”)： 水流、波纹。代表思绪的顺畅无阻。</span></span><span class="line"></span><span class="line"><span style="color: #61AFEF">概念</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">B</span><span style="color: #ABB2BF"> (侧重“专注与极简”)： 一个极其干净的几何形状，比如一个完美的圆点，或者一条抽象的线段。代表聚焦。</span></span><span class="line"></span><span class="line"><span style="color: #61AFEF">概念</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">C</span><span style="color: #ABB2BF"> (侧重“创造力”)： 抽象的羽毛笔尖，或者墨水晕开的瞬间。</span></span><span class="line"></span><span class="line"><span style="color: #61AFEF">我的建议方向：</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">结合“笔尖”与“心流”。一个抽象化的、流畅的线条组成的笔尖形状，看起来像是在流动。</span></span><span class="line"></span><span class="line"><span style="color: #61AFEF">👉</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">在实际流程中，这一步我会给你提供</span><span style="color: #ABB2BF"> </span><span style="color: #D19A66">2</span><span style="color: #98C379">-3</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">个创意方向供你选择，或者让你提供你脑海中浮现的第一个物体。</span></span><span class="line"></span><span class="line"><span style="color: #61AFEF">阶段三：风格与美学</span><span style="color: #ABB2BF"> (Aesthetics)</span></span><span class="line"><span style="color: #61AFEF">（以下是基于演示案例的分析）</span></span><span class="line"></span><span class="line"><span style="color: #61AFEF">设计师分析：</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">iOS</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">的设计趋势目前主要有几种：</span></span><span class="line"></span><span class="line"><span style="color: #61AFEF">现代拟物</span><span style="color: #ABB2BF"> (Modern </span><span style="color: #98C379">Skeuomorphism</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">/</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">Depth</span><span style="color: #ABB2BF">): 有微妙的渐变、柔和的阴影和层次感，看起来想触摸它。（Apple 原生应用多为此类，如相册、Safari）。</span></span><span class="line"></span><span class="line"><span style="color: #61AFEF">超级扁平</span><span style="color: #ABB2BF"> (Super </span><span style="color: #98C379">Flat</span><span style="color: #ABB2BF">): 没有任何质感和阴影，纯色块。（现在比较少见，容易显得廉价）。</span></span><span class="line"></span><span class="line"><span style="color: #61AFEF">3D</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">渲染</span><span style="color: #ABB2BF"> (3D </span><span style="color: #98C379">Render</span><span style="color: #ABB2BF">): 看起来像一个可爱的 3D 玩具模型，材质感很强。（常用于游戏或年轻化的应用）。</span></span><span class="line"></span><span class="line"><span style="color: #61AFEF">对于</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">FlowWrite，我建议采用</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">“现代拟物</span><span style="color: #ABB2BF"> (Modern </span><span style="color: #98C379">Depth</span><span style="color: #ABB2BF">)” 风格。它既保持了极简，又通过微妙的质感显得高级。</span></span><span class="line"></span><span class="line"><span style="color: #61AFEF">颜色：</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">既然是宁静的，我建议使用冷色调。例如：深海蓝渐变到青色，或者一个非常干净的米白色背景配上深灰色的主体。</span></span><span class="line"></span><span class="line"><span style="color: #61AFEF">👉</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">在实际流程中，你需要告诉我你偏好的风格类型和核心配色方案。</span></span><span class="line"></span><span class="line"><span style="color: #61AFEF">阶段四：Prompt</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">合成</span><span style="color: #ABB2BF"> (Synthesis)</span></span><span class="line"><span style="color: #61AFEF">（现在，我将把以上所有信息，转化为</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">AI</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">能听懂的专业术语。这是最关键的一步。）</span></span><span class="line"></span><span class="line"><span style="color: #61AFEF">基于演示案例</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">&quot;FlowWrite&quot;</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">的最终</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">Prompt：</span></span><span class="line"></span><span class="line"><span style="color: #61AFEF">Markdown</span></span><span class="line"></span><span class="line"><span style="color: #ABB2BF">**Prompt:**</span></span><span class="line"><span style="color: #61AFEF">An</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">iOS</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">app</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">icon</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">for</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">&quot;FlowWrite&quot;,</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">a</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">minimalist</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">writing</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">tool.</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">The</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">design</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">features</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">an</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">abstract,</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">fluid</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">line</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">forming</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">a</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">stylized</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">fountain</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">pen</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">nib,</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">suggesting</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">creative</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">flow</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">and</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">focus.</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">The</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">style</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">is</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">modern</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">Apple</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">aesthetic,</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">with</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">subtle</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">depth,</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">soft</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">gradients,</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">and</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">a</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">tactile</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">feel,</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">not</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">flat.</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">The</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">color</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">palette</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">uses</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">deep</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">ocean</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">blues</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">transitioning</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">to</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">soft</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">teal,</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">set</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">against</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">a</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">clean,</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">off-white</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">background.</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">The</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">composition</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">is</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">centered</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">and</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">balanced</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">within</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">a</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">squircle</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">shape.</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">High</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">quality</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">render,</span><span style="color: #ABB2BF"> </span><span style="color: #D19A66">4</span><span style="color: #98C379">k,</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">detailed</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">texture,</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">minimalist</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">design.</span><span style="color: #ABB2BF"> </span><span style="color: #D19A66">--ar</span><span style="color: #ABB2BF"> </span><span style="color: #D19A66">1</span><span style="color: #98C379">:1</span></span><span class="line"><span style="color: #61AFEF">Prompt</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">解析（设计师的秘密配方）：</span></span><span class="line"></span><span class="line"><span style="color: #61AFEF">主体明确:</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">&quot;An iOS app icon for...&quot;</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">开门见山定性。</span></span><span class="line"></span><span class="line"><span style="color: #61AFEF">核心隐喻:</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">&quot;abstract, fluid line forming a stylized fountain pen nib&quot;</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">描述了我们确定的符号。</span></span><span class="line"></span><span class="line"><span style="color: #61AFEF">风格界定:</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">&quot;modern Apple aesthetic, with subtle depth, soft gradients... not flat&quot;</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">这是为了防止</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">AI</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">生成过时的扁平化图标。</span></span><span class="line"></span><span class="line"><span style="color: #61AFEF">颜色指定:</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">&quot;deep ocean blues transitioning to soft teal&quot;</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">精确控制色彩氛围。</span></span><span class="line"></span><span class="line"><span style="color: #61AFEF">构图与容器:</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">&quot;centered... within a squircle shape&quot;</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">强调了</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">iOS</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">图标的物理形态。</span></span><span class="line"></span><span class="line"><span style="color: #61AFEF">质量增强词:</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">&quot;High quality render, 4k, detailed texture&quot;</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">保证输出的精细度。</span></span><span class="line"></span><span class="line"><span style="color: #61AFEF">现在，轮到你了。</span></span><span class="line"><span style="color: #61AFEF">请回到</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">“阶段一：核心定义”，回答那三个问题，我们开始创造属于你的</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">App</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">Icon。</span></span></code></pre></div></div></figure><p>From there, just answer the questions one by one. Eventually, you will get the final prompt to use for image generation. If the image doesn’t turn out quite right, just keep the conversation going to refine it and get a new prompt.</p><p>Hope everyone gets an image they’re happy with!</p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;&lt;img src=&quot;/img/038-using-nano-banana-pro-to-create-an-app-icon.md/68cdfcac4be6.png&quot;&gt;&lt;/p&gt;
&lt;figure class=&quot;shiki bash&quot;&gt;&lt;div class=&#39;codeblock</summary>
      
    
    
    
    <category term="Dev" scheme="https://ihugo.cc/categories/Dev/"/>
    
    
    <category term="nano-banana" scheme="https://ihugo.cc/tags/nano-banana/"/>
    
  </entry>
  
  <entry>
    <title>SwiftUI’s Unexpected Behaviours - A Case Study on Padding and Backgrounds</title>
    <link href="https://ihugo.cc/2025/11/23/037-003-swiftuis-unexpected-behaviours-a-case-study-on-padding-and-backgrounds/"/>
    <id>https://ihugo.cc/2025/11/23/037-003-swiftuis-unexpected-behaviours-a-case-study-on-padding-and-backgrounds/</id>
    <published>2025-11-23T22:13:00.000Z</published>
    <updated>2026-05-25T03:24:31.411Z</updated>
    
    <content type="html"><![CDATA[<p>Many developers complain about SwiftUI because some of its behaviours feel counter-intuitive. For example, in the code at #1, if you comment out the .padding() modifier, the entire area above the search field turns red. That happens because the background of the search view bleeds into its parent container. If you don’t want the Search view’s background to affect the parent view, you have to break that view hierarchy connection.</p><img src='/img/037-003-swiftuis-unexpected-behaviours-a-case-study-on-padding-and-backgrounds.md/162b286699a5.png' style="height:512px"><figure class="shiki swift"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">var</span><span style="color: #ABB2BF"> body: </span><span style="color: #C678DD">some</span><span style="color: #ABB2BF"> View {</span></span><span class="line"><span style="color: #ABB2BF">        </span><span style="color: #61AFEF">VStack</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">spacing</span><span style="color: #ABB2BF">: </span><span style="color: #D19A66">24</span><span style="color: #ABB2BF">) {</span></span><span class="line"><span style="color: #ABB2BF">            search</span></span><span class="line"><span style="color: #ABB2BF">            </span><span style="color: #61AFEF">Spacer</span><span style="color: #ABB2BF">()</span></span><span class="line"><span style="color: #ABB2BF">        }</span></span><span class="line"><span style="color: #7F848E; font-style: italic">//        .padding() // #1</span></span><span class="line"><span style="color: #ABB2BF">        .</span><span style="color: #61AFEF">background</span><span style="color: #ABB2BF">(ThoughtStreamAsset.</span><span style="color: #E06C75">Colors</span><span style="color: #ABB2BF">.</span><span style="color: #E06C75">bgPrimary</span><span style="color: #ABB2BF">.</span><span style="color: #E06C75">swiftUIColor</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #ABB2BF">    }</span></span><span class="line"><span style="color: #ABB2BF">    </span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">var</span><span style="color: #ABB2BF"> search: </span><span style="color: #C678DD">some</span><span style="color: #ABB2BF"> View {</span></span><span class="line"><span style="color: #ABB2BF">        </span><span style="color: #61AFEF">TextField</span><span style="color: #ABB2BF">(</span><span style="color: #98C379">&quot;Search&quot;</span><span style="color: #ABB2BF">, </span><span style="color: #61AFEF">text</span><span style="color: #ABB2BF">: $searchText, </span><span style="color: #61AFEF">prompt</span><span style="color: #ABB2BF">: </span><span style="color: #61AFEF">Text</span><span style="color: #ABB2BF">(</span><span style="color: #98C379">&quot;Search your knowledge base&quot;</span><span style="color: #ABB2BF">).</span><span style="color: #61AFEF">foregroundColor</span><span style="color: #ABB2BF">(.</span><span style="color: #E06C75">gray</span><span style="color: #ABB2BF">))</span></span><span class="line"><span style="color: #ABB2BF">            .</span><span style="color: #61AFEF">foregroundColor</span><span style="color: #ABB2BF">(ThoughtStreamAsset.</span><span style="color: #E06C75">Colors</span><span style="color: #ABB2BF">.</span><span style="color: #E06C75">textPrimary</span><span style="color: #ABB2BF">.</span><span style="color: #E06C75">swiftUIColor</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #ABB2BF">            .</span><span style="color: #61AFEF">padding</span><span style="color: #ABB2BF">()</span></span><span class="line"><span style="color: #ABB2BF">            .</span><span style="color: #61AFEF">background</span><span style="color: #ABB2BF">(Color.</span><span style="color: #E06C75">red</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #ABB2BF">    }</span></span></code></pre></div></div></figure><p>When the padding at #1 is not commented out, the layout renders as expected:</p><p>But when you remove the parent padding, SwiftUI changes how the backgroud is applied, causing the red search background to expand and cover areas you wouldn’t expect. These kinds of layout propagation rules are often why developers find SwiftUI unintuitive.</p><img src='/img/037-003-swiftuis-unexpected-behaviours-a-case-study-on-padding-and-backgrounds.md/d152f2ba0476.png' style="height:512px">]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;Many developers complain about SwiftUI because some of its behaviours feel counter-intuitive. For example, in the code at #1, if you comm</summary>
      
    
    
    
    <category term="Dev" scheme="https://ihugo.cc/categories/Dev/"/>
    
    
    <category term="iOS" scheme="https://ihugo.cc/tags/iOS/"/>
    
    <category term="Debug" scheme="https://ihugo.cc/tags/Debug/"/>
    
  </entry>
  
  <entry>
    <title>obtain the scrollView&#39;s offset</title>
    <link href="https://ihugo.cc/2025/11/19/036-002-reliably-obtain-the-scrollview-s-offset/"/>
    <id>https://ihugo.cc/2025/11/19/036-002-reliably-obtain-the-scrollview-s-offset/</id>
    <published>2025-11-19T23:30:00.000Z</published>
    <updated>2026-05-25T03:24:31.411Z</updated>
    
    <content type="html"><![CDATA[<p><img src="/img/036-002-reliably-obtain-the-scrollview-s-offset.md/c8a002e9c6d4.gif"></p><p>Before iOS 16, you could get the ScrollView’s offset using the following code. However, if the UI becomes complex, the scrollViewOffset will not update while scrolling.</p><figure class="shiki swift"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"><span style="color: #61AFEF">ScrollView</span><span style="color: #ABB2BF"> {</span></span><span class="line"><span style="color: #ABB2BF"></span><span style="color: #61AFEF">VStack</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">alignment</span><span style="color: #ABB2BF">: .</span><span style="color: #E06C75">leading</span><span style="color: #ABB2BF">, </span><span style="color: #61AFEF">spacing</span><span style="color: #ABB2BF">: </span><span style="color: #D19A66">32</span><span style="color: #ABB2BF">) {</span></span><span class="line"><span style="color: #ABB2BF"></span><span style="color: #7F848E; font-style: italic">// 1. 将监测器独立出来，包裹在无间距的 VStack 中以避免布局干扰</span></span><span class="line"><span style="color: #ABB2BF"></span><span style="color: #61AFEF">VStack</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">spacing</span><span style="color: #ABB2BF">: </span><span style="color: #D19A66">0</span><span style="color: #ABB2BF">) {</span></span><span class="line"><span style="color: #ABB2BF">Color.</span><span style="color: #E06C75">clear</span></span><span class="line"><span style="color: #ABB2BF">.</span><span style="color: #61AFEF">frame</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">height</span><span style="color: #ABB2BF">: </span><span style="color: #D19A66">0</span><span style="color: #ABB2BF">) </span><span style="color: #7F848E; font-style: italic">// 占据 0 高度，不可见但存在</span></span><span class="line"><span style="color: #ABB2BF">.</span><span style="color: #61AFEF">background</span><span style="color: #ABB2BF">(</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #61AFEF">GeometryReader</span><span style="color: #ABB2BF"> { proxy </span><span style="color: #C678DD">in</span></span><span class="line"><span style="color: #ABB2BF">        Color.</span><span style="color: #E06C75">clear</span></span><span class="line"><span style="color: #ABB2BF">            .</span><span style="color: #61AFEF">preference</span><span style="color: #ABB2BF">(</span></span><span class="line"><span style="color: #ABB2BF">                </span><span style="color: #61AFEF">key</span><span style="color: #ABB2BF">: ScrollOffsetKey.</span><span style="color: #C678DD">self</span><span style="color: #ABB2BF">,</span></span><span class="line"><span style="color: #ABB2BF">                </span><span style="color: #61AFEF">value</span><span style="color: #ABB2BF">: proxy.</span><span style="color: #61AFEF">frame</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">in</span><span style="color: #ABB2BF">: .</span><span style="color: #61AFEF">named</span><span style="color: #ABB2BF">(</span><span style="color: #98C379">&quot;scroll&quot;</span><span style="color: #ABB2BF">)).</span><span style="color: #E06C75">minY</span></span><span class="line"><span style="color: #ABB2BF">            )</span></span><span class="line"><span style="color: #ABB2BF">    }</span></span><span class="line"><span style="color: #ABB2BF">)</span></span><span class="line"></span><span class="line"><span style="color: #ABB2BF"></span><span style="color: #61AFEF">LargeNavBarView</span><span style="color: #ABB2BF">()</span></span><span class="line"><span style="color: #ABB2BF">.</span><span style="color: #61AFEF">opacity</span><span style="color: #ABB2BF">(largeHeaderOpacity)</span></span><span class="line"><span style="color: #ABB2BF">}</span></span><span class="line"></span><span class="line"><span style="color: #ABB2BF">ContentArea</span></span><span class="line"><span style="color: #ABB2BF">}</span></span><span class="line"><span style="color: #ABB2BF">.</span><span style="color: #61AFEF">padding</span><span style="color: #ABB2BF">(.</span><span style="color: #E06C75">horizontal</span><span style="color: #ABB2BF">, </span><span style="color: #D19A66">24</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #ABB2BF">}</span></span><span class="line"><span style="color: #ABB2BF">.</span><span style="color: #61AFEF">coordinateSpace</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">name</span><span style="color: #ABB2BF">: </span><span style="color: #98C379">&quot;scroll&quot;</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #ABB2BF">.</span><span style="color: #61AFEF">onPreferenceChange</span><span style="color: #ABB2BF">(ScrollOffsetKey.</span><span style="color: #C678DD">self</span><span style="color: #ABB2BF">) { value </span><span style="color: #C678DD">in</span><span style="color: #ABB2BF"> </span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #E5C07B">self</span><span style="color: #ABB2BF">.</span><span style="color: #E06C75">scrollOffset</span><span style="color: #ABB2BF"> = value </span></span><span class="line"><span style="color: #ABB2BF">}</span></span></code></pre></div></div></figure><p>After iOS 16, you can reliably obtain the scrollView’s offset using the following code.</p><figure class="shiki swift"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"><span style="color: #61AFEF">ScrollView</span><span style="color: #ABB2BF"> {</span></span><span class="line"><span style="color: #ABB2BF"></span><span style="color: #61AFEF">VStack</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">alignment</span><span style="color: #ABB2BF">: .</span><span style="color: #E06C75">leading</span><span style="color: #ABB2BF">, </span><span style="color: #61AFEF">spacing</span><span style="color: #ABB2BF">: </span><span style="color: #D19A66">32</span><span style="color: #ABB2BF">) {</span></span><span class="line"><span style="color: #ABB2BF"></span><span style="color: #7F848E; font-style: italic">// 1. 将监测器独立出来，包裹在无间距的 VStack 中以避免布局干扰</span></span><span class="line"><span style="color: #ABB2BF"></span><span style="color: #61AFEF">VStack</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">spacing</span><span style="color: #ABB2BF">: </span><span style="color: #D19A66">0</span><span style="color: #ABB2BF">) {</span></span><span class="line"><span style="color: #ABB2BF">Color.</span><span style="color: #E06C75">clear</span></span><span class="line"><span style="color: #ABB2BF">.</span><span style="color: #61AFEF">frame</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">height</span><span style="color: #ABB2BF">: </span><span style="color: #D19A66">0</span><span style="color: #ABB2BF">) </span><span style="color: #7F848E; font-style: italic">// 占据 0 高度，不可见但存在</span></span><span class="line"><span style="color: #ABB2BF">.</span><span style="color: #61AFEF">onGeometryChange</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">for</span><span style="color: #ABB2BF">: CGFloat.</span><span style="color: #C678DD">self</span><span style="color: #ABB2BF">) { proxy </span><span style="color: #C678DD">in</span></span><span class="line"><span style="color: #ABB2BF">proxy.</span><span style="color: #61AFEF">frame</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">in</span><span style="color: #ABB2BF">: .</span><span style="color: #61AFEF">named</span><span style="color: #ABB2BF">(</span><span style="color: #98C379">&quot;scroll&quot;</span><span style="color: #ABB2BF">)).</span><span style="color: #E06C75">minY</span></span><span class="line"><span style="color: #ABB2BF">} </span><span style="color: #61AFEF">action</span><span style="color: #ABB2BF">: { newValue </span><span style="color: #C678DD">in</span></span><span class="line"><span style="color: #ABB2BF"></span><span style="color: #E5C07B">self</span><span style="color: #ABB2BF">.</span><span style="color: #E06C75">scrollOffset</span><span style="color: #ABB2BF"> = newValue</span></span><span class="line"><span style="color: #ABB2BF"></span><span style="color: #56B6C2">print</span><span style="color: #ABB2BF">(</span><span style="color: #98C379">&quot;scrollOffset: </span><span style="color: #C678DD">\(</span><span style="color: #E5C07B">self</span><span style="color: #ABB2BF">.</span><span style="color: #E06C75">scrollOffset</span><span style="color: #C678DD">)</span><span style="color: #98C379">&quot;</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #ABB2BF">}</span></span><span class="line"></span><span class="line"><span style="color: #ABB2BF"></span><span style="color: #61AFEF">LargeNavBarView</span><span style="color: #ABB2BF">()</span></span><span class="line"><span style="color: #ABB2BF">.</span><span style="color: #61AFEF">opacity</span><span style="color: #ABB2BF">(largeHeaderOpacity)</span></span><span class="line"><span style="color: #ABB2BF">}</span></span><span class="line"></span><span class="line"><span style="color: #ABB2BF">ContentArea</span></span><span class="line"><span style="color: #ABB2BF">}</span></span><span class="line"><span style="color: #ABB2BF">.</span><span style="color: #61AFEF">padding</span><span style="color: #ABB2BF">(.</span><span style="color: #E06C75">horizontal</span><span style="color: #ABB2BF">, </span><span style="color: #D19A66">24</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #ABB2BF">}</span></span></code></pre></div></div></figure>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;&lt;img src=&quot;/img/036-002-reliably-obtain-the-scrollview-s-offset.md/c8a002e9c6d4.gif&quot;&gt;&lt;/p&gt;
&lt;p&gt;Before iOS 16, you could get the ScrollView’s</summary>
      
    
    
    
    <category term="Dev" scheme="https://ihugo.cc/categories/Dev/"/>
    
    
    <category term="iOS" scheme="https://ihugo.cc/tags/iOS/"/>
    
    <category term="Debug" scheme="https://ihugo.cc/tags/Debug/"/>
    
  </entry>
  
  <entry>
    <title>How to hide `tabbar` in iOS 18?</title>
    <link href="https://ihugo.cc/2025/11/12/035-001-how-to-hide-tabbar-in-ios-18/"/>
    <id>https://ihugo.cc/2025/11/12/035-001-how-to-hide-tabbar-in-ios-18/</id>
    <published>2025-11-12T23:41:00.000Z</published>
    <updated>2026-05-25T03:24:31.410Z</updated>
    
    <content type="html"><![CDATA[<p><img src="/img/035-001-how-to-hide-tabbar-in-ios-18.md/7f8990344968.gif"></p><p>I wrapped my view in a NavigationStack and used .toolbarVisibility(.hidden, for: .tabBar) to navigate while hiding the tab bar. However, this causes an issue: the transition animation becomes stiff and unnatural. I haven’t found a good solution yet.</p><p>I’m trying to research how Apple’s official apps handle this. It seems that many of them simply present a new view when they need to hide the tab bar.</p><p>The tab bar is generally always visible. When an app needs to hide it, the common pattern is to <strong>present a modal view</strong>, rather than pushing a view inside the existing navigation stack.<br><img src="/img/035-001-how-to-hide-tabbar-in-ios-18.md/fb5d4bdfefc9.gif"></p><p>The completed code is here:</p><figure class="shiki swift"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"><span style="color: #C678DD">import</span><span style="color: #ABB2BF"> </span><span style="color: #E5C07B">SwiftUI</span></span><span class="line"></span><span class="line"><span style="color: #C678DD">struct</span><span style="color: #ABB2BF"> </span><span style="color: #E5C07B">ContentView</span><span style="color: #ABB2BF">: </span><span style="color: #E5C07B">View </span><span style="color: #ABB2BF">{</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">var</span><span style="color: #ABB2BF"> body: </span><span style="color: #C678DD">some</span><span style="color: #ABB2BF"> View {</span></span><span class="line"><span style="color: #ABB2BF">        </span><span style="color: #61AFEF">TabView</span><span style="color: #ABB2BF"> {</span></span><span class="line"><span style="color: #ABB2BF">            </span><span style="color: #61AFEF">HomeTab</span><span style="color: #ABB2BF">()</span></span><span class="line"><span style="color: #ABB2BF">                .</span><span style="color: #61AFEF">tabItem</span><span style="color: #ABB2BF"> {</span></span><span class="line"><span style="color: #ABB2BF">                    </span><span style="color: #61AFEF">Label</span><span style="color: #ABB2BF">(</span><span style="color: #98C379">&quot;Home&quot;</span><span style="color: #ABB2BF">, </span><span style="color: #61AFEF">systemImage</span><span style="color: #ABB2BF">: </span><span style="color: #98C379">&quot;house.fill&quot;</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #ABB2BF">                }</span></span><span class="line"><span style="color: #ABB2BF">            </span></span><span class="line"><span style="color: #ABB2BF">            </span><span style="color: #61AFEF">Text</span><span style="color: #ABB2BF">(</span><span style="color: #98C379">&quot;Settings Tab&quot;</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #ABB2BF">                .</span><span style="color: #61AFEF">tabItem</span><span style="color: #ABB2BF"> {</span></span><span class="line"><span style="color: #ABB2BF">                    </span><span style="color: #61AFEF">Label</span><span style="color: #ABB2BF">(</span><span style="color: #98C379">&quot;Settings&quot;</span><span style="color: #ABB2BF">, </span><span style="color: #61AFEF">systemImage</span><span style="color: #ABB2BF">: </span><span style="color: #98C379">&quot;gear&quot;</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #ABB2BF">                }</span></span><span class="line"><span style="color: #ABB2BF">        }</span></span><span class="line"><span style="color: #ABB2BF">    }</span></span><span class="line"><span style="color: #ABB2BF">}</span></span><span class="line"></span><span class="line"><span style="color: #C678DD">struct</span><span style="color: #ABB2BF"> </span><span style="color: #E5C07B">HomeTab</span><span style="color: #ABB2BF">: </span><span style="color: #E5C07B">View </span><span style="color: #ABB2BF">{</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">var</span><span style="color: #ABB2BF"> body: </span><span style="color: #C678DD">some</span><span style="color: #ABB2BF"> View {</span></span><span class="line"><span style="color: #ABB2BF">        </span><span style="color: #61AFEF">NavigationStack</span><span style="color: #ABB2BF"> {</span></span><span class="line"><span style="color: #ABB2BF">            </span><span style="color: #61AFEF">MainView</span><span style="color: #ABB2BF">()</span></span><span class="line"><span style="color: #ABB2BF">        }</span></span><span class="line"><span style="color: #ABB2BF">    }</span></span><span class="line"><span style="color: #ABB2BF">}</span></span><span class="line"></span><span class="line"><span style="color: #C678DD">struct</span><span style="color: #ABB2BF"> </span><span style="color: #E5C07B">MainView</span><span style="color: #ABB2BF">: </span><span style="color: #E5C07B">View </span><span style="color: #ABB2BF">{</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">var</span><span style="color: #ABB2BF"> body: </span><span style="color: #C678DD">some</span><span style="color: #ABB2BF"> View {</span></span><span class="line"><span style="color: #ABB2BF">        </span><span style="color: #61AFEF">List</span><span style="color: #ABB2BF"> {</span></span><span class="line"><span style="color: #ABB2BF">            </span><span style="color: #61AFEF">NavigationLink</span><span style="color: #ABB2BF">(</span><span style="color: #98C379">&quot;Go to Detail Page 1&quot;</span><span style="color: #ABB2BF">) {</span></span><span class="line"><span style="color: #ABB2BF">                </span><span style="color: #61AFEF">DetailView</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">title</span><span style="color: #ABB2BF">: </span><span style="color: #98C379">&quot;Detail 1&quot;</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #ABB2BF">                    .</span><span style="color: #61AFEF">toolbarVisibility</span><span style="color: #ABB2BF">(.</span><span style="color: #E06C75">hidden</span><span style="color: #ABB2BF">, </span><span style="color: #61AFEF">for</span><span style="color: #ABB2BF">: .</span><span style="color: #E06C75">tabBar</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #ABB2BF">            }</span></span><span class="line"><span style="color: #ABB2BF">            </span><span style="color: #61AFEF">NavigationLink</span><span style="color: #ABB2BF">(</span><span style="color: #98C379">&quot;Go to Detail Page 2&quot;</span><span style="color: #ABB2BF">) {</span></span><span class="line"><span style="color: #ABB2BF">                </span><span style="color: #61AFEF">DetailView</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">title</span><span style="color: #ABB2BF">: </span><span style="color: #98C379">&quot;Detail 2&quot;</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #ABB2BF">            }</span></span><span class="line"><span style="color: #ABB2BF">        }</span></span><span class="line"><span style="color: #ABB2BF">        .</span><span style="color: #61AFEF">navigationTitle</span><span style="color: #ABB2BF">(</span><span style="color: #98C379">&quot;Main View&quot;</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #ABB2BF">    }</span></span><span class="line"><span style="color: #ABB2BF">}</span></span><span class="line"></span><span class="line"><span style="color: #C678DD">struct</span><span style="color: #ABB2BF"> </span><span style="color: #E5C07B">DetailView</span><span style="color: #ABB2BF">: </span><span style="color: #E5C07B">View </span><span style="color: #ABB2BF">{</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> title: </span><span style="color: #E5C07B">String</span></span><span class="line"><span style="color: #ABB2BF">    </span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">var</span><span style="color: #ABB2BF"> body: </span><span style="color: #C678DD">some</span><span style="color: #ABB2BF"> View {</span></span><span class="line"><span style="color: #ABB2BF">        </span><span style="color: #61AFEF">Text</span><span style="color: #ABB2BF">(</span><span style="color: #98C379">&quot;This is the </span><span style="color: #C678DD">\(</span><span style="color: #ABB2BF">title</span><span style="color: #C678DD">)</span><span style="color: #98C379">&quot;</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #ABB2BF">            .</span><span style="color: #61AFEF">navigationTitle</span><span style="color: #ABB2BF">(title)</span></span><span class="line"><span style="color: #ABB2BF">            .</span><span style="color: #61AFEF">navigationBarTitleDisplayMode</span><span style="color: #ABB2BF">(.</span><span style="color: #E06C75">inline</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #ABB2BF">    }</span></span><span class="line"><span style="color: #ABB2BF">}</span></span><span class="line"></span><span class="line"><span style="color: #61AFEF">#Preview</span><span style="color: #ABB2BF"> {</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #61AFEF">ContentView</span><span style="color: #ABB2BF">()</span></span><span class="line"><span style="color: #ABB2BF">}</span></span></code></pre></div></div></figure>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;&lt;img src=&quot;/img/035-001-how-to-hide-tabbar-in-ios-18.md/7f8990344968.gif&quot;&gt;&lt;/p&gt;
&lt;p&gt;I wrapped my view in a NavigationStack and used .toolbar</summary>
      
    
    
    
    <category term="Dev" scheme="https://ihugo.cc/categories/Dev/"/>
    
    
    <category term="iOS" scheme="https://ihugo.cc/tags/iOS/"/>
    
  </entry>
  
  <entry>
    <title>Re-understanding Swift Protocol</title>
    <link href="https://ihugo.cc/2025/11/10/033-re-understanding-swift-protocol/"/>
    <id>https://ihugo.cc/2025/11/10/033-re-understanding-swift-protocol/</id>
    <published>2025-11-10T02:38:00.000Z</published>
    <updated>2026-05-25T03:24:31.410Z</updated>
    
    <content type="html"><![CDATA[<h2 id="🧠-Introduction"><a href="#🧠-Introduction" class="headerlink" title="🧠 Introduction"></a><strong>🧠 Introduction</strong></h2><p>In Swift, <strong>Type Erasure</strong> is a key concept for understanding protocols and generics.</p><p>It explains why any exists and how Swift’s generic protocols differ from other languages.</p><hr><h2 id="1️⃣-Why-Type-Erasure-Matters"><a href="#1️⃣-Why-Type-Erasure-Matters" class="headerlink" title="1️⃣ Why Type Erasure Matters"></a><strong>1️⃣ Why Type Erasure Matters</strong></h2><figure class="shiki swift"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"><span style="color: #C678DD">protocol</span><span style="color: #ABB2BF"> </span><span style="color: #E5C07B">ReReduce</span><span style="color: #ABB2BF"> {}</span></span><span class="line"><span style="color: #C678DD">struct</span><span style="color: #ABB2BF"> </span><span style="color: #E5C07B">AppReduce</span><span style="color: #ABB2BF">: </span><span style="color: #E5C07B">ReReduce </span><span style="color: #ABB2BF">{}</span></span><span class="line"></span><span class="line"><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> a: </span><span style="color: #C678DD">any</span><span style="color: #ABB2BF"> ReReduce = </span><span style="color: #61AFEF">AppReduce</span><span style="color: #ABB2BF">()</span></span></code></pre></div></div></figure><p>any ReReduce means “a type that conforms to ReReduce,” , but the exact type is unknown at compile time.</p><p>The compiler performs <strong>type erasure</strong>, wrapping the value in an <strong>existential container</strong>. This allows storing and calling methods on multiple conforming types in one collection.</p><hr><h2 id="2️⃣-Generics-vs-Existential-Types"><a href="#2️⃣-Generics-vs-Existential-Types" class="headerlink" title="2️⃣ Generics vs Existential Types"></a><strong>2️⃣ Generics vs Existential Types</strong></h2><figure class="shiki swift"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"><span style="color: #C678DD">func</span><span style="color: #ABB2BF"> </span><span style="color: #61AFEF">process</span><span style="color: #ABB2BF">&lt;</span><span style="color: #E5C07B">T</span><span style="color: #ABB2BF">: </span><span style="color: #E5C07B">Reducable</span><span style="color: #ABB2BF">&gt;(</span><span style="color: #61AFEF">_</span><span style="color: #ABB2BF"> </span><span style="color: #ABB2BF; font-style: italic">value</span><span style="color: #ABB2BF">: T) { }   </span><span style="color: #7F848E; font-style: italic">// Generic</span></span><span class="line"><span style="color: #C678DD">func</span><span style="color: #ABB2BF"> </span><span style="color: #61AFEF">process</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">_</span><span style="color: #ABB2BF"> </span><span style="color: #ABB2BF; font-style: italic">value</span><span style="color: #ABB2BF">: </span><span style="color: #C678DD">any</span><span style="color: #ABB2BF"> Reducable) { }     </span><span style="color: #7F848E; font-style: italic">// Existential</span></span></code></pre></div></div></figure><table><thead><tr><th><strong>Feature</strong></th><th><strong>Generic</strong> <T></th><th><strong>Existential</strong> any</th></tr></thead><tbody><tr><td>Type known</td><td>Compile-time</td><td>Runtime</td></tr><tr><td>Dispatch</td><td>Static (fast)</td><td>Dynamic (erased)</td></tr><tr><td>Mixed types</td><td>No</td><td>Yes</td></tr><tr><td>Use case</td><td>Performance, type safety</td><td>Polymorphism, flexibility</td></tr></tbody></table><blockquote><p>Generics let the compiler <em>know</em> the type;<br>any lets it <em>forget</em> the type but keep its behavior.</p></blockquote><hr><h2 id="3️⃣-Swift-vs-Other-Languages"><a href="#3️⃣-Swift-vs-Other-Languages" class="headerlink" title="3️⃣ Swift vs Other Languages"></a><strong>3️⃣ Swift vs Other Languages</strong></h2><figure class="shiki swift"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"><span style="color: #C678DD">protocol</span><span style="color: #ABB2BF"> </span><span style="color: #E5C07B">ReReducer</span><span style="color: #ABB2BF"> {</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">associatedtype</span><span style="color: #ABB2BF"> </span><span style="color: #E5C07B">S</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">associatedtype</span><span style="color: #ABB2BF"> </span><span style="color: #E5C07B">A</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">func</span><span style="color: #ABB2BF"> </span><span style="color: #61AFEF">reduce</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">_</span><span style="color: #ABB2BF"> </span><span style="color: #ABB2BF; font-style: italic">state</span><span style="color: #ABB2BF">: S, </span><span style="color: #61AFEF">_</span><span style="color: #ABB2BF"> </span><span style="color: #ABB2BF; font-style: italic">action</span><span style="color: #ABB2BF">: A) -&gt; S</span></span><span class="line"><span style="color: #ABB2BF">}</span></span></code></pre></div></div></figure><p><strong>TypeScript equivalent:</strong></p><figure class="shiki typescript"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"><span style="color: #C678DD">interface</span><span style="color: #ABB2BF"> </span><span style="color: #E5C07B">ReReducer</span><span style="color: #ABB2BF">&lt;</span><span style="color: #E5C07B">S</span><span style="color: #ABB2BF">, </span><span style="color: #E5C07B">A</span><span style="color: #ABB2BF">&gt; {</span></span><span class="line"><span style="color: #ABB2BF">  </span><span style="color: #61AFEF">reduce</span><span style="color: #ABB2BF">(</span><span style="color: #E06C75; font-style: italic">state</span><span style="color: #ABB2BF">: </span><span style="color: #E5C07B">S</span><span style="color: #ABB2BF">, </span><span style="color: #E06C75; font-style: italic">action</span><span style="color: #ABB2BF">: </span><span style="color: #E5C07B">A</span><span style="color: #ABB2BF">): </span><span style="color: #E5C07B">S</span><span style="color: #ABB2BF">;</span></span><span class="line"><span style="color: #ABB2BF">}</span></span></code></pre></div></div></figure><table><thead><tr><th><strong>Feature</strong></th><th><strong>Swift</strong></th><th><strong>TypeScript &#x2F; C#</strong></th></tr></thead><tbody><tr><td>Generic style</td><td>associatedtype</td><td>&lt;T&gt;</td></tr><tr><td>Type decided</td><td>At implementation&#x2F;use</td><td>At declaration</td></tr><tr><td>Can store in collection</td><td>Requires type erasure</td><td>Directly supported</td></tr></tbody></table><p>Swift needs <strong>type erasure</strong> to mix generic implementations in one array.</p><hr><h2 id="4️⃣-Binding-Associated-Types"><a href="#4️⃣-Binding-Associated-Types" class="headerlink" title="4️⃣ Binding Associated Types"></a><strong>4️⃣ Binding Associated Types</strong></h2><p>You can fix associated types using typealias to unify them:</p><figure class="shiki swift"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"><span style="color: #C678DD">typealias</span><span style="color: #ABB2BF"> </span><span style="color: #E5C07B">AnyAppReducer</span><span style="color: #ABB2BF"> </span><span style="color: #56B6C2">=</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">any</span><span style="color: #ABB2BF"> ReReducer</span></span><span class="line"><span style="color: #ABB2BF">  </span><span style="color: #C678DD">where</span><span style="color: #ABB2BF"> S == AppState, A == AppAction</span></span><span class="line"></span><span class="line"><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> reducers: [AnyAppReducer] = [</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #61AFEF">AppReducer</span><span style="color: #ABB2BF">(),</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #61AFEF">AnotherAppReducer</span><span style="color: #ABB2BF">()</span></span><span class="line"><span style="color: #ABB2BF">]</span></span></code></pre></div></div></figure><hr><h2 id="🧩-Summary"><a href="#🧩-Summary" class="headerlink" title="🧩 Summary"></a><strong>🧩 Summary</strong></h2><table><thead><tr><th><strong>Concept</strong></th><th><strong>Meaning</strong></th></tr></thead><tbody><tr><td>any</td><td>Declares an existential type</td></tr><tr><td>Type Erasure</td><td>Hides concrete type for flexibility</td></tr><tr><td>Generics</td><td>Compile-time, high performance</td></tr><tr><td>Existentials</td><td>Runtime, dynamic and flexible</td></tr><tr><td>associatedtype</td><td>Defines generic protocols in Swift</td></tr></tbody></table><ul><li><strong>Use generics</strong> for performance and type safety.</li><li><strong>Use</strong> <strong>any</strong> for polymorphism and dynamic flexibility.</li></ul><p>Understanding type erasure helps you design clearer, more adaptable Swift architectures.</p>]]></content>
    
    
      
      
    <summary type="html">&lt;h2 id=&quot;🧠-Introduction&quot;&gt;&lt;a href=&quot;#🧠-Introduction&quot; class=&quot;headerlink&quot; title=&quot;🧠 Introduction&quot;&gt;&lt;/a&gt;&lt;strong&gt;🧠 Introduction&lt;/strong&gt;&lt;/h2&gt;&lt;p&gt;I</summary>
      
    
    
    
    <category term="Dev" scheme="https://ihugo.cc/categories/Dev/"/>
    
    
    <category term="iOS" scheme="https://ihugo.cc/tags/iOS/"/>
    
  </entry>
  
  <entry>
    <title>学习如何学习</title>
    <link href="https://ihugo.cc/2025/11/05/030-learn-how-to-learn/"/>
    <id>https://ihugo.cc/2025/11/05/030-learn-how-to-learn/</id>
    <published>2025-11-05T08:56:00.000Z</published>
    <updated>2026-05-25T03:24:31.410Z</updated>
    
    <content type="html"><![CDATA[<p>上一次认真思考“如何学习”这件事，还是在看 Scott 那本书的时候。他一年之内自学完了四年制大学的 CS 课程。最近我又开始有点迷茫。快四十了，后端不算真正精通，iOS 技能也荒废了，语言也没学好。这些问题叠在一起，让我压力很大。结果又开始玩 Dota 2 来逃避。</p><p>昨天在 YouTube 上看到一个讲「大脑如何高效学习」的频道 (Justin Sung)。里面提到：死记硬背是被动学习，效率非常低。真正有效的学习是把知识主动消化，融入自己的知识体系里。要想记得牢，必须不断从大脑里“取出来”——也就是使用、复述、应用。这也解释了为什么需要复习和回忆。</p><p>今天我也解决了 ANKI 卡片数量爆炸的问题。原来不是每一个定义都要做成一张卡片，而是要用“提问”来让自己从大脑中检索知识，让神经连接真正建立起来。</p><p>今天学习的时候，我尝试用 Obsidian 的 canvas 来组织知识。一边学习，一边把知识点画出来、连接起来。要学会一个东西，必须让大脑主动参与。要给知识编码、建立上下文，把它放进自己已有的认知架构里。否则大脑会直接把它当成“无用信息”丢掉。如果强行硬背，大脑还会直接“关机”，让你犯困。</p><p>明天继续早起。<br>上午学 iOS。<br>下午学英语。<br>晚上工作。<br>就这样，一点点把自己拉回正轨。</p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;上一次认真思考“如何学习”这件事，还是在看 Scott 那本书的时候。他一年之内自学完了四年制大学的 CS 课程。最近我又开始有点迷茫。快四十了，后端不算真正精通，iOS 技能也荒废了，语言也没学好。这些问题叠在一起，让我压力很大。结果又开始玩 Dota 2 来逃避。&lt;/p</summary>
      
    
    
    
    <category term="Life" scheme="https://ihugo.cc/categories/Life/"/>
    
    
    <category term="Life/Meta" scheme="https://ihugo.cc/tags/Life-Meta/"/>
    
  </entry>
  
  <entry>
    <title>一段有趣的代码之 swift 6 对于内存访问优化</title>
    <link href="https://ihugo.cc/2025/09/30/029-yi-duan-you-qu-de-dai-ma-zhi-swift-6-dui-yu-nei-cun-fang-wen-you-hua/"/>
    <id>https://ihugo.cc/2025/09/30/029-yi-duan-you-qu-de-dai-ma-zhi-swift-6-dui-yu-nei-cun-fang-wen-you-hua/</id>
    <published>2025-09-30T22:45:18.000Z</published>
    <updated>2026-05-25T03:24:31.410Z</updated>
    
    <content type="html"><![CDATA[<p>早上躺在床上挣扎起床, 拿起手机刷完邮件, 和工作IM之后忍不住打开了X. 看到一个tweet <a href="https://x.com/Lakr233/status/1972677999729512858">https://x.com/Lakr233/status/1972677999729512858</a>. 看了下, 随口说出了答案是200. 但是 @Lakr233 说他这个问题另有深意. 所以赶紧起床研究问题.</p><p><img src="/img/029-yi-duan-you-qu-de-dai-ma-zhi-swift-6-dui-yu-nei-cun-fang-wen-you-hua.md/3a96a09c7453.png"></p><p>我开始尝试关闭编译器优化, 重新编译运行. 同样能正常运行, 没有报<code>Simultaneous access</code>的运行时错误.</p><p><img src="/img/029-yi-duan-you-qu-de-dai-ma-zhi-swift-6-dui-yu-nei-cun-fang-wen-you-hua.md/3f81e2fb16fd.png"><br><img src="/img/029-yi-duan-you-qu-de-dai-ma-zhi-swift-6-dui-yu-nei-cun-fang-wen-you-hua.md/7c79e7376ca9.png"></p><p>为了对的起我的起床, 我决定深入研究一下. 我先简化代码, 然后阅读汇编代码.<br><img src="/img/029-yi-duan-you-qu-de-dai-ma-zhi-swift-6-dui-yu-nei-cun-fang-wen-you-hua.md/d549a74cebb8.png"><br><img src="/img/029-yi-duan-you-qu-de-dai-ma-zhi-swift-6-dui-yu-nei-cun-fang-wen-you-hua.md/80ff8f120811.png"></p><p>通过上图, 可以看到<code>take</code>方法的调用在<code>swift_beginsAccess</code>与 <code>swift_endAccess</code>之间, 说明swift确实检查了内存安全. 但是它只检查了一次, 并没有针对第8行的闭包和take方法分别检查, 所以我的程序能过正常运行. </p><p>对了,  swift_beginsAccess 方法会对访问内容进行检查, 如果都是读取操作不会有问题, 如果针对某个地址还有其它操作就会报运行时错误. </p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;早上躺在床上挣扎起床, 拿起手机刷完邮件, 和工作IM之后忍不住打开了X. 看到一个tweet &lt;a href=&quot;https://x.com/Lakr233/status/1972677999729512858&quot;&gt;https://x.com/Lakr233/status/1</summary>
      
    
    
    
    <category term="Dev" scheme="https://ihugo.cc/categories/Dev/"/>
    
    
    <category term="iOS" scheme="https://ihugo.cc/tags/iOS/"/>
    
  </entry>
  
  <entry>
    <title>confetti animation</title>
    <link href="https://ihugo.cc/2025/08/22/026-confetti-animation/"/>
    <id>https://ihugo.cc/2025/08/22/026-confetti-animation/</id>
    <published>2025-08-22T02:04:37.000Z</published>
    <updated>2026-05-25T03:24:31.410Z</updated>
    
    <content type="html"><![CDATA[<p><img src="/img/confetti-animation/demo.gif" alt="demo"></p><p>This post is about creating a confetti animation in SwiftUI with a customizable duration. I learned the technique from this <a href="https://www.patreon.com/posts/swiftui-tutorial-136704481?utm_medium=clipboard_copy&utm_source=copyLink&utm_campaign=postshare_creator&utm_content=join_link">Patreon tutorial</a>.</p><p>In the original animation, the confetti pieces would change color while falling, which I considered a bug. I’ve since fixed it. Another optimization I considered was adding a fade-out effect to make the disappearance of the confetti smoother, but the result wasn’t satisfactory, so I have not included that change.</p><p>Let’s break down how to build this.</p><h3 id="Step-1-Animate-a-Single-Piece-of-Confetti"><a href="#Step-1-Animate-a-Single-Piece-of-Confetti" class="headerlink" title="Step 1: Animate a Single Piece of Confetti"></a>Step 1: Animate a Single Piece of Confetti</h3><p>First, we’ll create the animation for a single piece of confetti. The code and the result are below:</p><p><img src="/img/confetti-animation/confetti.jpg" alt="confetti"></p><figure class="shiki swift"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"><span style="color: #C678DD">struct</span><span style="color: #ABB2BF"> </span><span style="color: #E5C07B">ConfettiView</span><span style="color: #ABB2BF">: </span><span style="color: #E5C07B">View </span><span style="color: #ABB2BF">{</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">@State</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">private</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">var</span><span style="color: #ABB2BF"> animate = </span><span style="color: #D19A66">false</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">@State</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">private</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">var</span><span style="color: #ABB2BF"> xSpeed = </span><span style="color: #E5C07B">Double</span><span style="color: #ABB2BF">.</span><span style="color: #61AFEF">random</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">in</span><span style="color: #ABB2BF">: </span><span style="color: #D19A66">0.7</span><span style="color: #ABB2BF">...</span><span style="color: #D19A66">2.0</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">@State</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">private</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">var</span><span style="color: #ABB2BF"> zSpeed = </span><span style="color: #E5C07B">Double</span><span style="color: #ABB2BF">.</span><span style="color: #61AFEF">random</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">in</span><span style="color: #ABB2BF">: </span><span style="color: #D19A66">1.0</span><span style="color: #ABB2BF">...</span><span style="color: #D19A66">2.0</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">@State</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">private</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">var</span><span style="color: #ABB2BF"> anchor = CGFloat.</span><span style="color: #61AFEF">random</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">in</span><span style="color: #ABB2BF">: </span><span style="color: #D19A66">0</span><span style="color: #ABB2BF">...</span><span style="color: #D19A66">1</span><span style="color: #ABB2BF">).</span><span style="color: #56B6C2">rounded</span><span style="color: #ABB2BF">()</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">@State</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">private</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">var</span><span style="color: #ABB2BF"> color: Color = [.</span><span style="color: #E06C75">orange</span><span style="color: #ABB2BF">, .</span><span style="color: #E06C75">green</span><span style="color: #ABB2BF">, .</span><span style="color: #E06C75">blue</span><span style="color: #ABB2BF">, .</span><span style="color: #E06C75">red</span><span style="color: #ABB2BF">, .</span><span style="color: #E06C75">yellow</span><span style="color: #ABB2BF">].</span><span style="color: #61AFEF">randomElement</span><span style="color: #ABB2BF">() ?? .</span><span style="color: #E06C75">green</span></span><span class="line"></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">var</span><span style="color: #ABB2BF"> body: </span><span style="color: #C678DD">some</span><span style="color: #ABB2BF"> View {</span></span><span class="line"><span style="color: #ABB2BF">        </span><span style="color: #61AFEF">Rectangle</span><span style="color: #ABB2BF">()</span></span><span class="line"><span style="color: #ABB2BF">            .</span><span style="color: #61AFEF">fill</span><span style="color: #ABB2BF">(color)</span></span><span class="line"><span style="color: #ABB2BF">            .</span><span style="color: #61AFEF">frame</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">width</span><span style="color: #ABB2BF">: </span><span style="color: #D19A66">14</span><span style="color: #ABB2BF">, </span><span style="color: #61AFEF">height</span><span style="color: #ABB2BF">: </span><span style="color: #D19A66">14</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #ABB2BF">            .</span><span style="color: #61AFEF">onAppear</span><span style="color: #ABB2BF"> { animate = </span><span style="color: #D19A66">true</span><span style="color: #ABB2BF"> }</span></span><span class="line"><span style="color: #ABB2BF">            .</span><span style="color: #61AFEF">rotation3DEffect</span><span style="color: #ABB2BF">(.</span><span style="color: #61AFEF">degrees</span><span style="color: #ABB2BF">(animate ? </span><span style="color: #D19A66">360</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">:</span><span style="color: #ABB2BF"> </span><span style="color: #D19A66">0</span><span style="color: #ABB2BF">), </span><span style="color: #61AFEF">axis</span><span style="color: #ABB2BF">: (</span><span style="color: #61AFEF">x</span><span style="color: #ABB2BF">: </span><span style="color: #D19A66">1</span><span style="color: #ABB2BF">, </span><span style="color: #61AFEF">y</span><span style="color: #ABB2BF">: </span><span style="color: #D19A66">0</span><span style="color: #ABB2BF">, </span><span style="color: #61AFEF">z</span><span style="color: #ABB2BF">: </span><span style="color: #D19A66">0</span><span style="color: #ABB2BF">))</span></span><span class="line"><span style="color: #ABB2BF">            .</span><span style="color: #61AFEF">animation</span><span style="color: #ABB2BF">(.</span><span style="color: #61AFEF">linear</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">duration</span><span style="color: #ABB2BF">: xSpeed).</span><span style="color: #61AFEF">repeatForever</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">autoreverses</span><span style="color: #ABB2BF">: </span><span style="color: #D19A66">false</span><span style="color: #ABB2BF">), </span><span style="color: #61AFEF">value</span><span style="color: #ABB2BF">: animate)</span></span><span class="line"><span style="color: #ABB2BF">            .</span><span style="color: #61AFEF">rotation3DEffect</span><span style="color: #ABB2BF">(.</span><span style="color: #61AFEF">degrees</span><span style="color: #ABB2BF">(animate ? </span><span style="color: #D19A66">360</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">:</span><span style="color: #ABB2BF"> </span><span style="color: #D19A66">0</span><span style="color: #ABB2BF">),</span></span><span class="line"><span style="color: #ABB2BF">                              </span><span style="color: #61AFEF">axis</span><span style="color: #ABB2BF">: (</span><span style="color: #61AFEF">x</span><span style="color: #ABB2BF">: </span><span style="color: #D19A66">0</span><span style="color: #ABB2BF">, </span><span style="color: #61AFEF">y</span><span style="color: #ABB2BF">: </span><span style="color: #D19A66">0</span><span style="color: #ABB2BF">, </span><span style="color: #61AFEF">z</span><span style="color: #ABB2BF">: </span><span style="color: #D19A66">1</span><span style="color: #ABB2BF">),</span></span><span class="line"><span style="color: #ABB2BF">                              </span><span style="color: #61AFEF">anchor</span><span style="color: #ABB2BF">: </span><span style="color: #61AFEF">UnitPoint</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">x</span><span style="color: #ABB2BF">: anchor, </span><span style="color: #61AFEF">y</span><span style="color: #ABB2BF">: anchor))</span></span><span class="line"><span style="color: #ABB2BF">            .</span><span style="color: #61AFEF">animation</span><span style="color: #ABB2BF">(.</span><span style="color: #61AFEF">linear</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">duration</span><span style="color: #ABB2BF">: zSpeed).</span><span style="color: #61AFEF">repeatForever</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">autoreverses</span><span style="color: #ABB2BF">: </span><span style="color: #D19A66">false</span><span style="color: #ABB2BF">), </span><span style="color: #61AFEF">value</span><span style="color: #ABB2BF">: animate)</span></span><span class="line"><span style="color: #ABB2BF">    }</span></span><span class="line"><span style="color: #ABB2BF">}</span></span></code></pre></div></div></figure><p>This line of code is the key to fixing the color-changing issue:</p><p><code>@State private var color: Color = [.orange, .green, .blue, .red, .yellow].randomElement() ?? .green</code></p><p>The color used to change because the original code would assign a new random color during the animation process. By using <code>@State</code> to store the color, we ensure its value is preserved for the view’s lifetime, preventing the color from changing unexpectedly.</p><h3 id="Step-2-Create-the-Confetti-Container"><a href="#Step-2-Create-the-Confetti-Container" class="headerlink" title="Step 2: Create the Confetti Container"></a>Step 2: Create the Confetti Container</h3><p>Next, we’ll create a container to hold 50 confetti pieces and manage their positioning.</p><figure class="shiki swift"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"><span style="color: #C678DD">struct</span><span style="color: #ABB2BF"> </span><span style="color: #E5C07B">ConfettiContainerView</span><span style="color: #ABB2BF">: </span><span style="color: #E5C07B">View </span><span style="color: #ABB2BF">{</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">var</span><span style="color: #ABB2BF"> count: </span><span style="color: #E5C07B">Int</span><span style="color: #ABB2BF"> = </span><span style="color: #D19A66">50</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">@State</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">private</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">var</span><span style="color: #ABB2BF"> ySeed: CGFloat = </span><span style="color: #D19A66">0</span></span><span class="line"></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">var</span><span style="color: #ABB2BF"> body: </span><span style="color: #C678DD">some</span><span style="color: #ABB2BF"> View {</span></span><span class="line"><span style="color: #ABB2BF">        </span><span style="color: #61AFEF">GeometryReader</span><span style="color: #ABB2BF"> { geo </span><span style="color: #C678DD">in</span></span><span class="line"><span style="color: #ABB2BF">            </span><span style="color: #61AFEF">ZStack</span><span style="color: #ABB2BF"> {</span></span><span class="line"><span style="color: #ABB2BF">                </span><span style="color: #61AFEF">ForEach</span><span style="color: #ABB2BF">(</span><span style="color: #D19A66">0</span><span style="color: #ABB2BF">..&lt;count, </span><span style="color: #61AFEF">id</span><span style="color: #ABB2BF">: \.</span><span style="color: #C678DD">self</span><span style="color: #ABB2BF">) { _ </span><span style="color: #C678DD">in</span></span><span class="line"><span style="color: #ABB2BF">                    </span><span style="color: #61AFEF">ConfettiView</span><span style="color: #ABB2BF">()</span></span><span class="line"><span style="color: #ABB2BF">                        .</span><span style="color: #61AFEF">position</span><span style="color: #ABB2BF">(</span></span><span class="line"><span style="color: #ABB2BF">                            </span><span style="color: #61AFEF">x</span><span style="color: #ABB2BF">: CGFloat.</span><span style="color: #61AFEF">random</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">in</span><span style="color: #ABB2BF">: </span><span style="color: #D19A66">0</span><span style="color: #ABB2BF">...</span><span style="color: #E06C75">geo</span><span style="color: #ABB2BF">.size.</span><span style="color: #E06C75">width</span><span style="color: #ABB2BF">),</span></span><span class="line"><span style="color: #ABB2BF">                            </span><span style="color: #61AFEF">y</span><span style="color: #ABB2BF">: ySeed == </span><span style="color: #D19A66">0</span><span style="color: #ABB2BF"> ? </span><span style="color: #D19A66">0</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">:</span><span style="color: #ABB2BF"> CGFloat.</span><span style="color: #61AFEF">random</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">in</span><span style="color: #ABB2BF">: </span><span style="color: #D19A66">0</span><span style="color: #ABB2BF">...</span><span style="color: #E06C75">geo</span><span style="color: #ABB2BF">.size.</span><span style="color: #E06C75">height</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #ABB2BF">                        )</span></span><span class="line"><span style="color: #ABB2BF">                }</span></span><span class="line"><span style="color: #ABB2BF">            }</span></span><span class="line"><span style="color: #ABB2BF">            .</span><span style="color: #61AFEF">ignoresSafeArea</span><span style="color: #ABB2BF">()</span></span><span class="line"><span style="color: #ABB2BF">            .</span><span style="color: #61AFEF">onAppear</span><span style="color: #ABB2BF"> {</span></span><span class="line"><span style="color: #ABB2BF">                ySeed = geo.size.</span><span style="color: #E06C75">height</span></span><span class="line"><span style="color: #ABB2BF">            }</span></span><span class="line"><span style="color: #ABB2BF">        }</span></span><span class="line"><span style="color: #ABB2BF">    }</span></span><span class="line"><span style="color: #ABB2BF">}</span></span></code></pre></div></div></figure><img height="400" src="/img/confetti-animation/confetti-container.jpg" alt="A view filled with multiple, randomly positioned confetti pieces."><h3 id="Step-3-Encapsulate-as-a-View-Modifier"><a href="#Step-3-Encapsulate-as-a-View-Modifier" class="headerlink" title="Step 3: Encapsulate as a View Modifier"></a>Step 3: Encapsulate as a View Modifier</h3><p>Finally, to make the animation reusable and easy to apply, we’ll wrap it in a <code>ViewModifier</code>.</p><figure class="shiki swift"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"><span style="color: #C678DD">struct</span><span style="color: #ABB2BF"> </span><span style="color: #E5C07B">ConfettiModifier</span><span style="color: #ABB2BF">: </span><span style="color: #E5C07B">ViewModifier </span><span style="color: #ABB2BF">{</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">@Binding</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">var</span><span style="color: #ABB2BF"> isActive: </span><span style="color: #E5C07B">Bool</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">@State</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">private</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">var</span><span style="color: #ABB2BF"> opacity = </span><span style="color: #D19A66">1.0</span></span><span class="line"></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">private</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> animationTime = </span><span style="color: #D19A66">3.0</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">private</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> fadeTime = </span><span style="color: #D19A66">1.6</span></span><span class="line"></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">func</span><span style="color: #ABB2BF"> </span><span style="color: #61AFEF">body</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF; font-style: italic">content</span><span style="color: #ABB2BF">: Content) -&gt; </span><span style="color: #C678DD">some</span><span style="color: #ABB2BF"> View {</span></span><span class="line"><span style="color: #ABB2BF">        content</span></span><span class="line"><span style="color: #ABB2BF">            .</span><span style="color: #61AFEF">overlay</span><span style="color: #ABB2BF">(</span></span><span class="line"><span style="color: #ABB2BF">                </span><span style="color: #61AFEF">ZStack</span><span style="color: #ABB2BF"> {</span></span><span class="line"><span style="color: #ABB2BF">                    </span><span style="color: #C678DD">if</span><span style="color: #ABB2BF"> isActive {</span></span><span class="line"><span style="color: #ABB2BF">                        </span><span style="color: #61AFEF">ConfettiContainerView</span><span style="color: #ABB2BF">()</span></span><span class="line"><span style="color: #ABB2BF">                            .</span><span style="color: #61AFEF">opacity</span><span style="color: #ABB2BF">(opacity)</span></span><span class="line"><span style="color: #ABB2BF">                            .</span><span style="color: #61AFEF">allowsHitTesting</span><span style="color: #ABB2BF">(</span><span style="color: #D19A66">false</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #ABB2BF">                    }</span></span><span class="line"><span style="color: #ABB2BF">                }</span></span><span class="line"><span style="color: #ABB2BF">            )</span></span><span class="line"><span style="color: #ABB2BF">            .</span><span style="color: #61AFEF">onChange</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">of</span><span style="color: #ABB2BF">: isActive) { _, newValue </span><span style="color: #C678DD">in</span></span><span class="line"><span style="color: #ABB2BF">                </span><span style="color: #C678DD">guard</span><span style="color: #ABB2BF"> newValue </span><span style="color: #C678DD">else</span><span style="color: #ABB2BF"> { </span><span style="color: #C678DD">return</span><span style="color: #ABB2BF"> }</span></span><span class="line"><span style="color: #ABB2BF">                </span><span style="color: #61AFEF">Task</span><span style="color: #ABB2BF"> {</span></span><span class="line"><span style="color: #ABB2BF">                    </span><span style="color: #C678DD">await</span><span style="color: #ABB2BF"> </span><span style="color: #56B6C2">sequence</span><span style="color: #ABB2BF">()</span></span><span class="line"><span style="color: #ABB2BF">                }</span></span><span class="line"><span style="color: #ABB2BF">            }</span></span><span class="line"><span style="color: #ABB2BF">    }</span></span><span class="line"></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">private</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">func</span><span style="color: #ABB2BF"> </span><span style="color: #61AFEF">sequence</span><span style="color: #ABB2BF">() </span><span style="color: #C678DD">async</span><span style="color: #ABB2BF"> {</span></span><span class="line"><span style="color: #ABB2BF">        </span><span style="color: #C678DD">do</span><span style="color: #ABB2BF"> {</span></span><span class="line"><span style="color: #ABB2BF">            </span><span style="color: #C678DD">try</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">await</span><span style="color: #ABB2BF"> Task.</span><span style="color: #61AFEF">sleep</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">nanoseconds</span><span style="color: #ABB2BF">: </span><span style="color: #E5C07B">UInt64</span><span style="color: #ABB2BF">(animationTime * </span><span style="color: #D19A66">1_000_000_000</span><span style="color: #ABB2BF">))</span></span><span class="line"><span style="color: #ABB2BF">            </span></span><span class="line"><span style="color: #ABB2BF">            </span><span style="color: #61AFEF">withAnimation</span><span style="color: #ABB2BF">(.</span><span style="color: #61AFEF">easeOut</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">duration</span><span style="color: #ABB2BF">: fadeTime)) {</span></span><span class="line"><span style="color: #ABB2BF">                opacity = </span><span style="color: #D19A66">0</span></span><span class="line"><span style="color: #ABB2BF">            }</span></span><span class="line"><span style="color: #ABB2BF">            </span></span><span class="line"><span style="color: #ABB2BF">            </span><span style="color: #C678DD">try</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">await</span><span style="color: #ABB2BF"> Task.</span><span style="color: #61AFEF">sleep</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">nanoseconds</span><span style="color: #ABB2BF">: </span><span style="color: #E5C07B">UInt64</span><span style="color: #ABB2BF">(fadeTime * </span><span style="color: #D19A66">1_000_000_000</span><span style="color: #ABB2BF">))</span></span><span class="line"><span style="color: #ABB2BF">            isActive = </span><span style="color: #D19A66">false</span></span><span class="line"><span style="color: #ABB2BF">            opacity = </span><span style="color: #D19A66">1.0</span></span><span class="line"><span style="color: #ABB2BF">        } </span><span style="color: #C678DD">catch</span><span style="color: #ABB2BF"> {}</span></span><span class="line"><span style="color: #ABB2BF">    }</span></span><span class="line"><span style="color: #ABB2BF">}</span></span><span class="line"></span><span class="line"><span style="color: #C678DD">extension</span><span style="color: #ABB2BF"> </span><span style="color: #E5C07B">View</span><span style="color: #ABB2BF"> {</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">func</span><span style="color: #ABB2BF"> </span><span style="color: #61AFEF">displayConfetti</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF; font-style: italic">isActive</span><span style="color: #ABB2BF">: Binding&lt;</span><span style="color: #E5C07B">Bool</span><span style="color: #ABB2BF">&gt;) -&gt; </span><span style="color: #C678DD">some</span><span style="color: #ABB2BF"> View {</span></span><span class="line"><span style="color: #ABB2BF">        </span><span style="color: #61AFEF">modifier</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">ConfettiModifier</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">isActive</span><span style="color: #ABB2BF">: isActive))</span></span><span class="line"><span style="color: #ABB2BF">    }</span></span><span class="line"><span style="color: #ABB2BF">}</span></span><span class="line"></span><span class="line"><span style="color: #7F848E; font-style: italic">// Usage Example</span></span><span class="line"><span style="color: #C678DD">struct</span><span style="color: #ABB2BF"> </span><span style="color: #E5C07B">ContentView</span><span style="color: #ABB2BF">: </span><span style="color: #E5C07B">View </span><span style="color: #ABB2BF">{</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">@State</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">private</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">var</span><span style="color: #ABB2BF"> showConfetti = </span><span style="color: #D19A66">false</span></span><span class="line"></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">var</span><span style="color: #ABB2BF"> body: </span><span style="color: #C678DD">some</span><span style="color: #ABB2BF"> View {</span></span><span class="line"><span style="color: #ABB2BF">        </span><span style="color: #61AFEF">VStack</span><span style="color: #ABB2BF"> {</span></span><span class="line"><span style="color: #ABB2BF">            </span><span style="color: #61AFEF">Text</span><span style="color: #ABB2BF">(</span><span style="color: #98C379">&quot;You did it!&quot;</span><span style="color: #ABB2BF">).</span><span style="color: #61AFEF">font</span><span style="color: #ABB2BF">(.</span><span style="color: #E06C75">title</span><span style="color: #ABB2BF">.</span><span style="color: #61AFEF">bold</span><span style="color: #ABB2BF">())</span></span><span class="line"><span style="color: #ABB2BF">            </span><span style="color: #61AFEF">Button</span><span style="color: #ABB2BF">(</span><span style="color: #98C379">&quot;Celebrate&quot;</span><span style="color: #ABB2BF">) {</span></span><span class="line"><span style="color: #ABB2BF">                showConfetti = </span><span style="color: #D19A66">true</span></span><span class="line"><span style="color: #ABB2BF">            }</span></span><span class="line"><span style="color: #ABB2BF">        }</span></span><span class="line"><span style="color: #ABB2BF">        .</span><span style="color: #61AFEF">frame</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">maxWidth</span><span style="color: #ABB2BF">: .infinity, </span><span style="color: #61AFEF">maxHeight</span><span style="color: #ABB2BF">: .infinity)</span></span><span class="line"><span style="color: #ABB2BF">        .</span><span style="color: #61AFEF">displayConfetti</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">isActive</span><span style="color: #ABB2BF">: $showConfetti)</span></span><span class="line"><span style="color: #ABB2BF">    }</span></span><span class="line"><span style="color: #ABB2BF">}</span></span></code></pre></div></div></figure>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;&lt;img src=&quot;/img/confetti-animation/demo.gif&quot; alt=&quot;demo&quot;&gt;&lt;/p&gt;
&lt;p&gt;This post is about creating a confetti animation in SwiftUI with a customi</summary>
      
    
    
    
    <category term="Dev" scheme="https://ihugo.cc/categories/Dev/"/>
    
    
    <category term="iOS" scheme="https://ihugo.cc/tags/iOS/"/>
    
  </entry>
  
  <entry>
    <title>Take On / Off Light Animation</title>
    <link href="https://ihugo.cc/2025/08/20/025-Take-On-Off-Light-Animation/"/>
    <id>https://ihugo.cc/2025/08/20/025-Take-On-Off-Light-Animation/</id>
    <published>2025-08-20T22:45:18.000Z</published>
    <updated>2026-05-25T03:24:31.410Z</updated>
    
    <content type="html"><![CDATA[<p>Sometimes, you have to explore different paths to find where you truly belong. For a developer, that path is often paved with different languages, frameworks, and platforms. My own journey has taken me through the worlds of backend development, cross-platform apps with React Native and Flutter, and of course, native iOS. And after seeing what each has to offer, I’ve come to a clear and exciting realization: my true passion lies in building for native iOS.</p><p><img src="https://github.com/iHugo-Tang/swiftui-practice/blob/main/assets/take-light-off.gif?raw=true" alt="TakeOffLight"></p><h2 id="A-Journey-Across-the-Stack"><a href="#A-Journey-Across-the-Stack" class="headerlink" title="A Journey Across the Stack"></a><strong>A Journey Across the Stack</strong></h2><p>Every technology I’ve worked with has taught me something invaluable.</p><ul><li>Building <strong>backend systems</strong> gave me a deep appreciation for data architecture, APIs, and the logic that powers applications from behind the scenes.</li><li>Working with <strong>React Native and Flutter</strong> was a fantastic lesson in efficiency and the challenge of creating a consistent experience across different operating systems. The promise of “write once, run anywhere” is compelling, and I learned a great deal about managing a single codebase for multiple targets.</li></ul><p>This broad perspective is something I wouldn’t trade. It gave me a holistic view of how a product comes to life, from the database all the way to the user’s screen. But it also created a point of comparison that continually highlighted what makes native development, and specifically iOS, so special to me.</p><h2 id="The-Pull-of-Native-iOS"><a href="#The-Pull-of-Native-iOS" class="headerlink" title="The Pull of Native iOS"></a><strong>The Pull of Native iOS</strong></h2><p>There’s a certain elegance and satisfaction in iOS development that I kept coming back to. The seamless integration between the Swift language, powerful frameworks like SwiftUI, and the hardware itself allows for a level of polish and performance that is simply a joy to create. The pursuit of the perfect animation, the crispness of a native UI component, and the satisfaction of building something that feels completely at home on the device—that’s what excites me as a developer.</p><p>After reflecting on this, I felt a renewed surge of energy and inspiration. To channel it into something tangible, I decided to build a small, focused project that captures the kind of delightful interaction I love: a simple “lights-out” animation.</p><h2 id="Project-The-“Lights-Out”-Animation"><a href="#Project-The-“Lights-Out”-Animation" class="headerlink" title="Project: The “Lights-Out” Animation"></a><strong>Project: The “Lights-Out” Animation</strong></h2><p>I wanted to create more than just a toggle. I wanted to build an experience. The idea was to mimic the satisfying, physical act of pulling a cord to turn a light on and off, complete with animated light beams, a draggable cord, and a crisp sound effect.</p><p><a href="https://github.com/iHugo-Tang/swiftui-practice/raw/refs/heads/main/assets/take-light-off.mp4">Here is a video of the final result!</a></p><p>This project, while small, was a great way to put SwiftUI’s strengths to the test, focusing on:</p><ul><li><strong>Declarative UI:</strong> Building complex views that react to state changes.</li><li><strong>State Management:</strong> Using <code>@State</code> and <code>@Binding</code> to drive the entire UI from a single source of truth (<code>isOn</code>).</li><li><strong>Animation:</strong> Leveraging <code>withAnimation</code> and <code>animation</code> modifiers to create fluid transitions and spring physics for the pull cord.</li><li><strong>Gestures:</strong> Implementing a <code>DragGesture</code> to create an interactive and intuitive pull-cord mechanism.</li></ul><h3 id="A-Look-at-the-Code"><a href="#A-Look-at-the-Code" class="headerlink" title="A Look at the Code"></a><strong>A Look at the Code</strong></h3><p>For those interested in how it works, the full source code is available on my GitHub. But here are a few key pieces that bring the experience to life.</p><p>The core of the app is the <code>ContentView</code>, which manages the <code>isOn</code> state. This single boolean drives everything from the background color to the sound playback.</p><figure class="shiki swift"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"><span style="color: #C678DD">struct</span><span style="color: #ABB2BF"> </span><span style="color: #E5C07B">ContentView</span><span style="color: #ABB2BF">: </span><span style="color: #E5C07B">View </span><span style="color: #ABB2BF">{</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">@State</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">var</span><span style="color: #ABB2BF"> isOn = </span><span style="color: #D19A66">true</span></span><span class="line"><span style="color: #ABB2BF">    </span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">var</span><span style="color: #ABB2BF"> body: </span><span style="color: #C678DD">some</span><span style="color: #ABB2BF"> View {</span></span><span class="line"><span style="color: #ABB2BF">        </span><span style="color: #61AFEF">ZStack</span><span style="color: #ABB2BF"> {</span></span><span class="line"><span style="color: #ABB2BF">            </span><span style="color: #7F848E; font-style: italic">// The background&#39;s gradient changes based on the &#39;isOn&#39; state</span></span><span class="line"><span style="color: #ABB2BF">            </span><span style="color: #61AFEF">LightBeamBackground</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">isOn</span><span style="color: #ABB2BF">: </span><span style="color: #E5C07B">self</span><span style="color: #ABB2BF">.$isOn)</span></span><span class="line"><span style="color: #ABB2BF">                .</span><span style="color: #61AFEF">ignoresSafeArea</span><span style="color: #ABB2BF">()</span></span><span class="line"><span style="color: #ABB2BF">            </span></span><span class="line"><span style="color: #ABB2BF">            </span><span style="color: #7F848E; font-style: italic">// The main lightbulb button that can also toggle the state</span></span><span class="line"><span style="color: #ABB2BF">            </span><span style="color: #61AFEF">Button</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">action</span><span style="color: #ABB2BF">: {</span></span><span class="line"><span style="color: #ABB2BF">                </span><span style="color: #61AFEF">withAnimation</span><span style="color: #ABB2BF"> {</span></span><span class="line"><span style="color: #ABB2BF">                    </span><span style="color: #E5C07B">self</span><span style="color: #ABB2BF">.</span><span style="color: #E06C75">isOn</span><span style="color: #ABB2BF">.</span><span style="color: #61AFEF">toggle</span><span style="color: #ABB2BF">()</span></span><span class="line"><span style="color: #ABB2BF">                }</span></span><span class="line"><span style="color: #ABB2BF">            }) {</span></span><span class="line"><span style="color: #ABB2BF">                </span><span style="color: #7F848E; font-style: italic">// ... button content</span></span><span class="line"><span style="color: #ABB2BF">            }</span></span><span class="line"><span style="color: #ABB2BF">        }</span></span><span class="line"><span style="color: #ABB2BF">        .</span><span style="color: #61AFEF">overlay</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">alignment</span><span style="color: #ABB2BF">: .</span><span style="color: #E06C75">topTrailing</span><span style="color: #ABB2BF">) {</span></span><span class="line"><span style="color: #ABB2BF">            </span><span style="color: #7F848E; font-style: italic">// The interactive pull cord is an overlay</span></span><span class="line"><span style="color: #ABB2BF">            </span><span style="color: #61AFEF">PullCord</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">isOn</span><span style="color: #ABB2BF">: </span><span style="color: #E5C07B">self</span><span style="color: #ABB2BF">.$isOn)</span></span><span class="line"><span style="color: #ABB2BF">                .</span><span style="color: #61AFEF">padding</span><span style="color: #ABB2BF">(.</span><span style="color: #E06C75">top</span><span style="color: #ABB2BF">, </span><span style="color: #D19A66">-40</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #ABB2BF">        }</span></span><span class="line"><span style="color: #ABB2BF">        .</span><span style="color: #61AFEF">onChange</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">of</span><span style="color: #ABB2BF">: </span><span style="color: #E5C07B">self</span><span style="color: #ABB2BF">.</span><span style="color: #E06C75">isOn</span><span style="color: #ABB2BF">) { _, newValue </span><span style="color: #C678DD">in</span></span><span class="line"><span style="color: #ABB2BF">            </span><span style="color: #7F848E; font-style: italic">// Play a sound effect whenever the state changes</span></span><span class="line"><span style="color: #ABB2BF">            SoundPlayer.</span><span style="color: #E06C75">shared</span><span style="color: #ABB2BF">.</span><span style="color: #61AFEF">play</span><span style="color: #ABB2BF">(newValue ? .</span><span style="color: #E06C75">lightOn</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">:</span><span style="color: #ABB2BF"> .</span><span style="color: #E06C75">lightOff</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #ABB2BF">        }</span></span><span class="line"><span style="color: #ABB2BF">    }</span></span><span class="line"><span style="color: #ABB2BF">}</span></span></code></pre></div></div></figure><p>The most interactive piece is the <code>PullCord</code> view. It uses a <code>DragGesture</code> to track the user’s finger and provides physical feedback by stretching. When the drag is released, it decides whether to toggle the light based on how far it was pulled.</p><figure class="shiki swift"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"><span style="color: #C678DD">struct</span><span style="color: #ABB2BF"> </span><span style="color: #E5C07B">PullCord</span><span style="color: #ABB2BF">: </span><span style="color: #E5C07B">View </span><span style="color: #ABB2BF">{</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">@Binding</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">var</span><span style="color: #ABB2BF"> isOn: </span><span style="color: #E5C07B">Bool</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">@State</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">private</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">var</span><span style="color: #ABB2BF"> dragOffsetY: CGFloat = </span><span style="color: #D19A66">0</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">private</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> maxDragDistance: CGFloat = </span><span style="color: #D19A66">140</span></span><span class="line"><span style="color: #ABB2BF">    </span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">var</span><span style="color: #ABB2BF"> body: </span><span style="color: #C678DD">some</span><span style="color: #ABB2BF"> View {</span></span><span class="line"><span style="color: #ABB2BF">        </span><span style="color: #61AFEF">ZStack</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">alignment</span><span style="color: #ABB2BF">: .</span><span style="color: #E06C75">top</span><span style="color: #ABB2BF">) {</span></span><span class="line"><span style="color: #ABB2BF">            </span><span style="color: #7F848E; font-style: italic">// ... visual components for the cord and handle</span></span><span class="line"><span style="color: #ABB2BF">        }</span></span><span class="line"><span style="color: #ABB2BF">        .</span><span style="color: #61AFEF">gesture</span><span style="color: #ABB2BF">(</span></span><span class="line"><span style="color: #ABB2BF">            </span><span style="color: #61AFEF">DragGesture</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">minimumDistance</span><span style="color: #ABB2BF">: </span><span style="color: #D19A66">0</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #ABB2BF">                .</span><span style="color: #61AFEF">onChanged</span><span style="color: #ABB2BF"> { value </span><span style="color: #C678DD">in</span></span><span class="line"><span style="color: #ABB2BF">                    </span><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> dy = </span><span style="color: #56B6C2">max</span><span style="color: #ABB2BF">(</span><span style="color: #D19A66">0</span><span style="color: #ABB2BF">, value.</span><span style="color: #E06C75">translation</span><span style="color: #ABB2BF">.</span><span style="color: #E06C75">height</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #ABB2BF">                    dragOffsetY = </span><span style="color: #56B6C2">min</span><span style="color: #ABB2BF">(dy, maxDragDistance)</span></span><span class="line"><span style="color: #ABB2BF">                }</span></span><span class="line"><span style="color: #ABB2BF">                .</span><span style="color: #61AFEF">onEnded</span><span style="color: #ABB2BF"> { value </span><span style="color: #C678DD">in</span></span><span class="line"><span style="color: #ABB2BF">                    </span><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> shouldToggle = value.</span><span style="color: #E06C75">translation</span><span style="color: #ABB2BF">.</span><span style="color: #E06C75">height</span><span style="color: #ABB2BF"> &gt; maxDragDistance * </span><span style="color: #D19A66">0.6</span></span><span class="line"><span style="color: #ABB2BF">                    </span><span style="color: #61AFEF">withAnimation</span><span style="color: #ABB2BF">(.</span><span style="color: #61AFEF">spring</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">response</span><span style="color: #ABB2BF">: </span><span style="color: #D19A66">0.35</span><span style="color: #ABB2BF">, </span><span style="color: #61AFEF">dampingFraction</span><span style="color: #ABB2BF">: </span><span style="color: #D19A66">0.8</span><span style="color: #ABB2BF">)) {</span></span><span class="line"><span style="color: #ABB2BF">                        dragOffsetY = </span><span style="color: #D19A66">0</span></span><span class="line"><span style="color: #ABB2BF">                        </span><span style="color: #C678DD">if</span><span style="color: #ABB2BF"> shouldToggle {</span></span><span class="line"><span style="color: #ABB2BF">                            isOn.</span><span style="color: #61AFEF">toggle</span><span style="color: #ABB2BF">()</span></span><span class="line"><span style="color: #ABB2BF">                        }</span></span><span class="line"><span style="color: #ABB2BF">                    }</span></span><span class="line"><span style="color: #ABB2BF">                }</span></span><span class="line"><span style="color: #ABB2BF">        )</span></span><span class="line"><span style="color: #ABB2BF">    }</span></span><span class="line"><span style="color: #ABB2BF">}</span></span></code></pre></div></div></figure><p>This project reaffirmed my belief in the power and elegance of SwiftUI for creating these kinds of delightful, polished user experiences.</p><h2 id="What’s-Next"><a href="#What’s-Next" class="headerlink" title="What’s Next"></a><strong>What’s Next</strong></h2><p>This journey of exploration across the tech stack has been invaluable, but now I know where I want to build my future. I’m currently based in <strong>Calgary</strong> and am actively seeking my next role as an <strong>iOS Developer</strong>. I’m looking for a team where I can contribute my diverse experience, my passion for Apple’s ecosystem, and my drive to build beautiful, high-performing applications.</p><p>If you’re looking for a passionate iOS developer with a broad technical perspective, I would love to connect. You can find my LinkedIn profile <a href="https://www.linkedin.com/in/haibo-tang-4a915531a/">here</a>.</p><p>Good night, Calgary. Hopeful for new opportunities and interviews tomorrow!</p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;Sometimes, you have to explore different paths to find where you truly belong. For a developer, that path is often paved with different l</summary>
      
    
    
    
    <category term="Dev" scheme="https://ihugo.cc/categories/Dev/"/>
    
    
    <category term="iOS" scheme="https://ihugo.cc/tags/iOS/"/>
    
  </entry>
  
  <entry>
    <title>binding-with-casepath</title>
    <link href="https://ihugo.cc/2025/08/20/024-binding-with-casepath/"/>
    <id>https://ihugo.cc/2025/08/20/024-binding-with-casepath/</id>
    <published>2025-08-20T06:18:50.000Z</published>
    <updated>2026-05-25T03:24:31.410Z</updated>
    
    <content type="html"><![CDATA[<h3 id="Advanced-Techniques-with-Binding-Transforming-and-Adapting-State"><a href="#Advanced-Techniques-with-Binding-Transforming-and-Adapting-State" class="headerlink" title="Advanced Techniques with Binding: Transforming and Adapting State"></a>Advanced Techniques with <code>Binding</code>: Transforming and Adapting State</h3><p>While the standard property wrappers handle most state management needs, you will often encounter situations where the <em>shape</em> of your state doesn’t perfectly match the requirements of a SwiftUI view. For example, a view might need a <code>Binding&lt;String&gt;</code>, but your model provides a <code>Binding&lt;String?&gt;</code>. Or a view needs to bind to the associated value of an <code>enum</code> case.</p><p>SwiftUI’s <code>Binding</code> type is incredibly powerful, and with a few extension methods, we can transform bindings to fit our exact needs. Let’s use the following data model for our examples:</p><figure class="shiki swift"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"><span style="color: #C678DD">struct</span><span style="color: #ABB2BF"> </span><span style="color: #E5C07B">Item</span><span style="color: #ABB2BF">: </span><span style="color: #E5C07B">Hashable</span><span style="color: #ABB2BF">, </span><span style="color: #E5C07B">Identifiable </span><span style="color: #ABB2BF">{</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> id = </span><span style="color: #61AFEF">UUID</span><span style="color: #ABB2BF">()</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">var</span><span style="color: #ABB2BF"> color: Color?</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">var</span><span style="color: #ABB2BF"> status: Status</span></span><span class="line"><span style="color: #ABB2BF">    </span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">enum</span><span style="color: #ABB2BF"> </span><span style="color: #E5C07B">Status</span><span style="color: #ABB2BF">: </span><span style="color: #E5C07B">Hashable </span><span style="color: #ABB2BF">{</span></span><span class="line"><span style="color: #ABB2BF">        </span><span style="color: #C678DD">case</span><span style="color: #ABB2BF"> </span><span style="color: #E06C75">inStock</span><span style="color: #ABB2BF">(</span><span style="color: #ABB2BF; font-style: italic">quantity</span><span style="color: #ABB2BF">: </span><span style="color: #E5C07B">Int</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #ABB2BF">        </span><span style="color: #C678DD">case</span><span style="color: #ABB2BF"> </span><span style="color: #E06C75">outOfStock</span><span style="color: #ABB2BF">(</span><span style="color: #ABB2BF; font-style: italic">isOnBackOrder</span><span style="color: #ABB2BF">: </span><span style="color: #E5C07B">Bool</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #ABB2BF">    }</span></span><span class="line"><span style="color: #ABB2BF">}</span></span></code></pre></div></div></figure><p>Given a state variable like <code>@State var item: Item</code>, a binding to its status, <code>$item.status</code>, would have the type <code>Binding&lt;Item.Status&gt;</code>. Let’s see how we can manipulate this and other bindings.</p><h3 id="Drilling-Down-into-Bindings-with-Key-Paths"><a href="#Drilling-Down-into-Bindings-with-Key-Paths" class="headerlink" title="Drilling Down into Bindings with Key Paths"></a>Drilling Down into Bindings with Key Paths</h3><p>SwiftUI has built-in support for creating bindings to the properties of a bound value. When you write <code>$item.status</code>, Swift is transparently applying a transform to map the <code>Binding&lt;Item&gt;</code> to a <code>Binding&lt;Item.Status&gt;</code>. This is conceptually achieved through a <code>map</code> function that uses a <code>WritableKeyPath</code>:</p><figure class="shiki swift"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"><span style="color: #C678DD">extension</span><span style="color: #ABB2BF"> </span><span style="color: #E5C07B">Binding</span><span style="color: #ABB2BF"> {</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">func</span><span style="color: #ABB2BF"> </span><span style="color: #61AFEF">map</span><span style="color: #ABB2BF">&lt;</span><span style="color: #E5C07B">LocalValue</span><span style="color: #ABB2BF">&gt;(</span><span style="color: #61AFEF">_</span><span style="color: #ABB2BF"> </span><span style="color: #ABB2BF; font-style: italic">keyPath</span><span style="color: #ABB2BF">: WritableKeyPath&lt;</span><span style="color: #E5C07B">Value</span><span style="color: #ABB2BF">, LocalValue&gt;) -&gt; Binding&lt;LocalValue&gt; {</span></span><span class="line"><span style="color: #ABB2BF">        </span><span style="color: #E5C07B">self</span><span style="color: #ABB2BF">[</span><span style="color: #61AFEF">dynamicMember</span><span style="color: #ABB2BF">: keyPath]</span></span><span class="line"><span style="color: #ABB2BF">    }</span></span><span class="line"><span style="color: #ABB2BF">}</span></span></code></pre></div></div></figure><p>You rarely need to call this <code>map</code> function directly, as the <code>.</code> syntax (<code>$item.status</code>) is convenient shorthand for the same operation. It’s the simplest way to transform a <code>Binding&lt;A&gt;</code> into a <code>Binding&lt;B&gt;</code>.</p><h3 id="Handling-Optionals-The-unwrap-Extension"><a href="#Handling-Optionals-The-unwrap-Extension" class="headerlink" title="Handling Optionals: The unwrap Extension"></a>Handling Optionals: The <code>unwrap</code> Extension</h3><p>A very common scenario is dealing with optional state. For instance, our <code>item.color</code> property is a <code>Color?</code>, making <code>$item.color</code> a <code>Binding&lt;Color?&gt;</code>. However, SwiftUI’s <code>ColorPicker</code> view requires a <code>Binding&lt;Color&gt;</code>. It cannot work with optionals.</p><p>To bridge this gap, we can create a handy <code>unwrap</code> extension that transforms an optional binding <code>Binding&lt;A?&gt;</code> into a non-optional binding <code>Binding&lt;A&gt;?</code>. The result is itself an optional: it will be <code>nil</code> if the original state is <code>nil</code>, or a valid, non-optional <code>Binding</code> if the state has a value.</p><figure class="shiki swift"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"><span style="color: #C678DD">extension</span><span style="color: #ABB2BF"> </span><span style="color: #E5C07B">Binding</span><span style="color: #ABB2BF"> {</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #7F848E; font-style: italic">/// Transforms a `Binding&lt;Wrapped?&gt;` into a `Binding&lt;Wrapped&gt;?`.</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #7F848E; font-style: italic">/// If the original binding&#39;s value is `nil`, this returns `nil`.</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">func</span><span style="color: #ABB2BF"> </span><span style="color: #61AFEF">unwrap</span><span style="color: #ABB2BF">&lt;</span><span style="color: #E5C07B">Wrapped</span><span style="color: #ABB2BF">&gt;() -&gt; Binding&lt;Wrapped&gt;? </span><span style="color: #C678DD">where</span><span style="color: #ABB2BF"> </span><span style="color: #E5C07B">Value</span><span style="color: #ABB2BF"> == Wrapped? {</span></span><span class="line"><span style="color: #ABB2BF">        </span><span style="color: #7F848E; font-style: italic">// If the wrapped value is nil, we can&#39;t create a binding.</span></span><span class="line"><span style="color: #ABB2BF">        </span><span style="color: #C678DD">guard</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> value = </span><span style="color: #E5C07B">self</span><span style="color: #ABB2BF">.wrappedValue </span><span style="color: #C678DD">else</span><span style="color: #ABB2BF"> { </span><span style="color: #C678DD">return</span><span style="color: #ABB2BF"> </span><span style="color: #D19A66">nil</span><span style="color: #ABB2BF"> }</span></span><span class="line"><span style="color: #ABB2BF">        </span></span><span class="line"><span style="color: #ABB2BF">        </span><span style="color: #7F848E; font-style: italic">// Create a new binding that gets the non-optional value</span></span><span class="line"><span style="color: #ABB2BF">        </span><span style="color: #7F848E; font-style: italic">// and sets the optional value on the original source.</span></span><span class="line"><span style="color: #ABB2BF">        </span><span style="color: #C678DD">return</span><span style="color: #ABB2BF"> Binding&lt;Wrapped&gt;(</span></span><span class="line"><span style="color: #ABB2BF">            </span><span style="color: #61AFEF">get</span><span style="color: #ABB2BF">: { value },</span></span><span class="line"><span style="color: #ABB2BF">            </span><span style="color: #61AFEF">set</span><span style="color: #ABB2BF">: { </span><span style="color: #E5C07B">self</span><span style="color: #ABB2BF">.</span><span style="color: #E06C75">wrappedValue</span><span style="color: #ABB2BF"> = </span><span style="color: #E5C07B">$0</span><span style="color: #ABB2BF"> }</span></span><span class="line"><span style="color: #ABB2BF">        )</span></span><span class="line"><span style="color: #ABB2BF">    }</span></span><span class="line"><span style="color: #ABB2BF">}</span></span></code></pre></div></div></figure><p><strong>Usage:</strong></p><p>You can use this with an <code>if let</code> statement to conditionally show a view that requires a non-optional binding.</p><figure class="shiki swift"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"><span style="color: #C678DD">struct</span><span style="color: #ABB2BF"> </span><span style="color: #E5C07B">ItemEditorView</span><span style="color: #ABB2BF">: </span><span style="color: #E5C07B">View </span><span style="color: #ABB2BF">{</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">@Binding</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">var</span><span style="color: #ABB2BF"> item: Item</span></span><span class="line"></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">var</span><span style="color: #ABB2BF"> body: </span><span style="color: #C678DD">some</span><span style="color: #ABB2BF"> View {</span></span><span class="line"><span style="color: #ABB2BF">        </span><span style="color: #7F848E; font-style: italic">// Only show the ColorPicker if a binding to a non-optional Color can be created.</span></span><span class="line"><span style="color: #ABB2BF">        </span><span style="color: #C678DD">if</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> colorBinding = $item.color.</span><span style="color: #61AFEF">unwrap</span><span style="color: #ABB2BF">() {</span></span><span class="line"><span style="color: #ABB2BF">            </span><span style="color: #61AFEF">ColorPicker</span><span style="color: #ABB2BF">(</span><span style="color: #98C379">&quot;Item Color&quot;</span><span style="color: #ABB2BF">, </span><span style="color: #61AFEF">selection</span><span style="color: #ABB2BF">: colorBinding)</span></span><span class="line"><span style="color: #ABB2BF">        } </span><span style="color: #C678DD">else</span><span style="color: #ABB2BF"> {</span></span><span class="line"><span style="color: #ABB2BF">            </span><span style="color: #61AFEF">Text</span><span style="color: #ABB2BF">(</span><span style="color: #98C379">&quot;This item has no color set.&quot;</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #ABB2BF">        }</span></span><span class="line"><span style="color: #ABB2BF">    }</span></span><span class="line"><span style="color: #ABB2BF">}</span></span></code></pre></div></div></figure><h3 id="A-More-General-Solution-for-Enums-matching-with-CasePaths"><a href="#A-More-General-Solution-for-Enums-matching-with-CasePaths" class="headerlink" title="A More General Solution for Enums: matching with CasePaths"></a>A More General Solution for Enums: <code>matching</code> with CasePaths</h3><p>The <code>unwrap</code> function is actually a specific version of a broader problem: how do we bind to the associated value of an <code>enum</code> case? An <code>Optional</code> is just an enum with two cases: <code>.none</code> and <code>.some(Wrapped)</code>. <code>unwrap</code> effectively extracts the associated value from the <code>.some</code> case.</p><p>To create a more generic solution for any enum, we can leverage the excellent <strong>CasePaths</strong> library. A <code>CasePath</code> is like a “key path for an enum case,” allowing you to reliably extract associated values from and embed them back into an enum.</p><p>Building on this, we can create a <code>matching</code> function that returns a binding to an associated value if and only if the binding’s value is currently in that specific case.</p><figure class="shiki swift"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"><span style="color: #7F848E; font-style: italic">// Requires the &#39;CasePaths&#39; library: https://github.com/pointfreeco/swift-case-paths</span></span><span class="line"><span style="color: #C678DD">import</span><span style="color: #ABB2BF"> </span><span style="color: #E5C07B">CasePaths</span></span><span class="line"></span><span class="line"><span style="color: #C678DD">extension</span><span style="color: #ABB2BF"> </span><span style="color: #E5C07B">Binding</span><span style="color: #ABB2BF"> {</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #7F848E; font-style: italic">/// Returns a new binding focused on a specific case of an enum.</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #7F848E; font-style: italic">/// If the original binding&#39;s value does not match the case, this returns `nil`.</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">func</span><span style="color: #ABB2BF"> </span><span style="color: #61AFEF">matching</span><span style="color: #ABB2BF">&lt;</span><span style="color: #E5C07B">Case</span><span style="color: #ABB2BF">&gt;(</span></span><span class="line"><span style="color: #ABB2BF">        </span><span style="color: #61AFEF">_</span><span style="color: #ABB2BF"> </span><span style="color: #ABB2BF; font-style: italic">casePath</span><span style="color: #ABB2BF">: CasePath&lt;</span><span style="color: #E5C07B">Value</span><span style="color: #ABB2BF">, Case&gt;</span></span><span class="line"><span style="color: #ABB2BF">    ) -&gt; Binding&lt;Case&gt;? {</span></span><span class="line"><span style="color: #ABB2BF">        </span><span style="color: #7F848E; font-style: italic">// Attempt to extract the associated value from the current state.</span></span><span class="line"><span style="color: #ABB2BF">        </span><span style="color: #C678DD">guard</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> `</span><span style="color: #C678DD">case</span><span style="color: #ABB2BF">` = casePath.</span><span style="color: #61AFEF">extract</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">from</span><span style="color: #ABB2BF">: </span><span style="color: #E5C07B">self</span><span style="color: #ABB2BF">.</span><span style="color: #E06C75">wrappedValue</span><span style="color: #ABB2BF">) </span><span style="color: #C678DD">else</span><span style="color: #ABB2BF"> { </span><span style="color: #C678DD">return</span><span style="color: #ABB2BF"> </span><span style="color: #D19A66">nil</span><span style="color: #ABB2BF"> }</span></span><span class="line"><span style="color: #ABB2BF">        </span></span><span class="line"><span style="color: #ABB2BF">        </span><span style="color: #7F848E; font-style: italic">// Create a new binding.</span></span><span class="line"><span style="color: #ABB2BF">        </span><span style="color: #C678DD">return</span><span style="color: #ABB2BF"> Binding&lt;Case&gt;(</span></span><span class="line"><span style="color: #ABB2BF">            </span><span style="color: #7F848E; font-style: italic">// The getter returns the extracted associated value.</span></span><span class="line"><span style="color: #ABB2BF">            </span><span style="color: #61AFEF">get</span><span style="color: #ABB2BF">: { `</span><span style="color: #C678DD">case</span><span style="color: #ABB2BF">` },</span></span><span class="line"><span style="color: #ABB2BF">            </span><span style="color: #7F848E; font-style: italic">// The setter embeds the new value back into the case and updates the original source.</span></span><span class="line"><span style="color: #ABB2BF">            </span><span style="color: #61AFEF">set</span><span style="color: #ABB2BF">: { `</span><span style="color: #C678DD">case</span><span style="color: #ABB2BF">` </span><span style="color: #C678DD">in</span><span style="color: #ABB2BF"> </span><span style="color: #E5C07B">self</span><span style="color: #ABB2BF">.</span><span style="color: #E06C75">wrappedValue</span><span style="color: #ABB2BF"> = casePath.</span><span style="color: #61AFEF">embed</span><span style="color: #ABB2BF">(`</span><span style="color: #C678DD">case</span><span style="color: #ABB2BF">`) }</span></span><span class="line"><span style="color: #ABB2BF">        )</span></span><span class="line"><span style="color: #ABB2BF">    }</span></span><span class="line"><span style="color: #ABB2BF">}</span></span></code></pre></div></div></figure><p><strong>Usage:</strong></p><p>This powerful extension allows you to build UI that adapts to the state of an enum. For our <code>Item.Status</code>, we can show completely different controls for each case.</p><figure class="shiki swift"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"><span style="color: #C678DD">struct</span><span style="color: #ABB2BF"> </span><span style="color: #E5C07B">ItemStatusView</span><span style="color: #ABB2BF">: </span><span style="color: #E5C07B">View </span><span style="color: #ABB2BF">{</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">@Binding</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">var</span><span style="color: #ABB2BF"> status: Item.Status</span></span><span class="line"><span style="color: #ABB2BF">    </span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">var</span><span style="color: #ABB2BF"> body: </span><span style="color: #C678DD">some</span><span style="color: #ABB2BF"> View {</span></span><span class="line"><span style="color: #ABB2BF">        </span><span style="color: #61AFEF">VStack</span><span style="color: #ABB2BF"> {</span></span><span class="line"><span style="color: #ABB2BF">            </span><span style="color: #7F848E; font-style: italic">// This UI will only appear and work if status is .inStock</span></span><span class="line"><span style="color: #ABB2BF">            </span><span style="color: #C678DD">if</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> quantityBinding = $status.</span><span style="color: #61AFEF">matching</span><span style="color: #ABB2BF">(/Item.</span><span style="color: #E06C75">Status</span><span style="color: #ABB2BF">.</span><span style="color: #E06C75">inStock</span><span style="color: #ABB2BF">) {</span></span><span class="line"><span style="color: #ABB2BF">                </span><span style="color: #61AFEF">Stepper</span><span style="color: #ABB2BF">(</span><span style="color: #98C379">&quot;Quantity: </span><span style="color: #C678DD">\(</span><span style="color: #ABB2BF">quantityBinding.</span><span style="color: #E06C75">wrappedValue</span><span style="color: #C678DD">)</span><span style="color: #98C379">&quot;</span><span style="color: #ABB2BF">, </span><span style="color: #61AFEF">value</span><span style="color: #ABB2BF">: quantityBinding)</span></span><span class="line"><span style="color: #ABB2BF">            }</span></span><span class="line"><span style="color: #ABB2BF">            </span></span><span class="line"><span style="color: #ABB2BF">            </span><span style="color: #7F848E; font-style: italic">// This UI will only appear and work if status is .outOfStock</span></span><span class="line"><span style="color: #ABB2BF">            </span><span style="color: #C678DD">if</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> onBackOrderBinding = $status.</span><span style="color: #61AFEF">matching</span><span style="color: #ABB2BF">(/Item.</span><span style="color: #E06C75">Status</span><span style="color: #ABB2BF">.</span><span style="color: #E06C75">outOfStock</span><span style="color: #ABB2BF">) {</span></span><span class="line"><span style="color: #ABB2BF">                </span><span style="color: #61AFEF">Toggle</span><span style="color: #ABB2BF">(</span><span style="color: #98C379">&quot;Is on backorder&quot;</span><span style="color: #ABB2BF">, </span><span style="color: #61AFEF">isOn</span><span style="color: #ABB2BF">: onBackOrderBinding)</span></span><span class="line"><span style="color: #ABB2BF">            }</span></span><span class="line"><span style="color: #ABB2BF">        }</span></span><span class="line"><span style="color: #ABB2BF">    }</span></span><span class="line"><span style="color: #ABB2BF">}</span></span></code></pre></div></div></figure><p>These techniques for transforming <code>Binding</code> are essential for writing clean, decoupled SwiftUI code. They allow your views to remain simple and focused on their specific data requirements, while your data models can remain complex and robust.</p>]]></content>
    
    
      
      
    <summary type="html">&lt;h3 id=&quot;Advanced-Techniques-with-Binding-Transforming-and-Adapting-State&quot;&gt;&lt;a href=&quot;#Advanced-Techniques-with-Binding-Transforming-and-Adapti</summary>
      
    
    
    
    <category term="Dev" scheme="https://ihugo.cc/categories/Dev/"/>
    
    
    <category term="iOS" scheme="https://ihugo.cc/tags/iOS/"/>
    
  </entry>
  
  <entry>
    <title>swiftui-state-management</title>
    <link href="https://ihugo.cc/2025/08/20/000-swiftui-state-management/"/>
    <id>https://ihugo.cc/2025/08/20/000-swiftui-state-management/</id>
    <published>2025-08-20T04:55:45.000Z</published>
    <updated>2026-05-25T03:24:31.409Z</updated>
    
    <content type="html"><![CDATA[<h2 id="A-Comprehensive-Guide-to-State-Management-in-SwiftUI"><a href="#A-Comprehensive-Guide-to-State-Management-in-SwiftUI" class="headerlink" title="A Comprehensive Guide to State Management in SwiftUI"></a>A Comprehensive Guide to State Management in SwiftUI</h2><p>In SwiftUI, managing the state of your application—the data that drives your UI—is a fundamental concept. SwiftUI provides a set of powerful property wrappers that handle view updates automatically when your data changes. This guide explores the core tools: <code>@State</code>, <code>@StateObject</code>, <code>@ObservedObject</code>, and <code>@Published</code>.</p><h3 id="Core-Property-Wrappers-at-a-Glance"><a href="#Core-Property-Wrappers-at-a-Glance" class="headerlink" title="Core Property Wrappers at a Glance"></a>Core Property Wrappers at a Glance</h3><p>The following table provides a quick reference for the most common state management property wrappers in SwiftUI. Note that <code>@StateObject</code>, <code>@ObservedObject</code>, and <code>@Published</code> are all integral parts of the Combine framework, which SwiftUI uses for reactive programming.</p><table><thead><tr><th align="left">Property Wrapper</th><th align="left">Purpose</th><th align="left">Data Type</th><th align="left">Ownership &amp; Lifecycle</th><th align="left">Primary Use Case</th></tr></thead><tbody><tr><td align="left"><strong><code>@State</code></strong></td><td align="left">Manages simple, private state within a single view.</td><td align="left">Value types (e.g., <code>Struct</code>, <code>Enum</code>, <code>Int</code>, <code>String</code>, <code>Bool</code>).</td><td align="left">Owned and managed by the view. SwiftUI manages its storage. It may be re-initialized if the view’s <strong>identity</strong> changes in the view hierarchy. It is <strong>view-local</strong>.</td><td align="left">Controlling the local state of UI components, such as a <code>Toggle</code>‘s on&#x2F;off state, a <code>TextField</code>‘s input, or whether an alert is shown.</td></tr><tr><td align="left"><strong><code>@StateObject</code></strong></td><td align="left">Manages a complex, private state object within a single view.</td><td align="left">Reference types (<code>Class</code>) that must conform to <code>ObservableObject</code>.</td><td align="left"><strong>Owned &amp; Persisted by the View.</strong> SwiftUI ensures the object’s instance persists for the lifetime of the view’s identity, even across redraws. It can be passed to other views.</td><td align="left">Creating and managing an instance of a complex data model (like a ViewModel) <strong>within the view that owns it</strong>.</td></tr><tr><td align="left"><strong><code>@ObservedObject</code></strong></td><td align="left">Subscribes to an existing observable object from an external source.</td><td align="left">Reference types (<code>Class</code>) that must conform to <code>ObservableObject</code>.</td><td align="left">The view <strong>does not own</strong> the object; it merely “borrows” or “observes” it. Its lifecycle is managed externally.</td><td align="left">Receiving and responding to a shared data model in a subview, where the model is managed by a parent view or another part of the app.</td></tr><tr><td align="left"><strong><code>@Published</code></strong></td><td align="left">Automatically publishes notifications when a property’s value changes.</td><td align="left">Any type.</td><td align="left">Its lifecycle is tied to the <code>ObservableObject</code> instance it belongs to.</td><td align="left">Marking properties within a ViewModel or shared data model that should trigger UI updates whenever they are modified.</td></tr></tbody></table><p>A key distinction to remember is that a view’s <code>@State</code> can be destroyed and recreated if the view is removed and re-added to the view hierarchy. In contrast, <code>@StateObject</code> is designed to survive view redraws as long as the view maintains its identity.</p><h3 id="Understanding-the-Nature-of-SwiftUI-Views"><a href="#Understanding-the-Nature-of-SwiftUI-Views" class="headerlink" title="Understanding the Nature of SwiftUI Views"></a>Understanding the Nature of SwiftUI Views</h3><p>Before diving deeper, it’s crucial to understand what a SwiftUI <code>View</code> is. The <code>struct</code> you define (e.g., <code>struct MyView: View</code>) is <strong>not</strong> the persistent object you see on screen. Instead, it’s a lightweight <strong>“blueprint” or “description”</strong> of your UI.</p><ul><li><strong>View Structs are Ephemeral:</strong> Every time SwiftUI needs to update the UI (perhaps because a <code>@State</code> variable changed), it re-creates your view struct and calls its <code>body</code> property to get a new blueprint. Creating and destroying these structs is extremely fast and low-cost.</li><li><strong>“Redraw” &#x3D; “Re-evaluating <code>body</code>“:</strong> When we say a “view redraws,” it’s more accurate to say that “the view’s <code>body</code> property is re-evaluated,” which often results in new view structs being created.</li></ul><h3 id="The-Magic-of-StateObject-Separating-State-from-the-View-Struct"><a href="#The-Magic-of-StateObject-Separating-State-from-the-View-Struct" class="headerlink" title="The Magic of @StateObject: Separating State from the View Struct"></a>The Magic of <code>@StateObject</code>: Separating State from the View Struct</h3><p><code>@StateObject</code> was introduced to solve the problem of state being reset during view redraws. Its mechanism works as follows:</p><ol><li><p><strong>View Identity:</strong> SwiftUI uniquely identifies a view by its <strong>position and type</strong> within the <strong>View Tree</strong>. For example, “the first <code>UserProfileView</code> inside the <code>VStack</code> in <code>ContentView</code>.”</p></li><li><p><strong>First-Time Creation &amp; Storage:</strong> The very first time a view with this specific identity appears, SwiftUI sees the <code>@StateObject</code> property wrapper. It then:</p><ul><li>Executes your initialization code (e.g., <code>_viewModel = StateObject(wrappedValue: UserViewModel())</code>) to create an instance of your <code>ObservableObject</code>.</li><li><strong>SwiftUI then takes this newly created instance and stores it in a special, managed memory area associated with that specific view identity.</strong></li></ul></li><li><p><strong>Subsequent Redraws:</strong> Later, when a parent view’s state changes and your view’s <code>body</code> is re-evaluated, a new view <em>struct</em> is created. However:</p><ul><li>SwiftUI again sees the <code>@StateObject</code> property wrapper.</li><li>This time, it checks its internal storage and finds that an object is already associated with this view’s identity.</li><li><strong>It skips your initialization code</strong> and simply connects the property to the pre-existing instance from its managed memory.</li></ul></li></ol><h3 id="Practical-Example-The-Lifecycle-of-State"><a href="#Practical-Example-The-Lifecycle-of-State" class="headerlink" title="Practical Example: The Lifecycle of @State"></a>Practical Example: The Lifecycle of <code>@State</code></h3><p>The following code demonstrates how <code>@State</code> is tied to the view instance’s lifetime within the view hierarchy.</p><figure class="shiki swift"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"><span style="color: #C678DD">import</span><span style="color: #ABB2BF"> </span><span style="color: #E5C07B">SwiftUI</span></span><span class="line"></span><span class="line"><span style="color: #C678DD">struct</span><span style="color: #ABB2BF"> </span><span style="color: #E5C07B">CounterView</span><span style="color: #ABB2BF">: </span><span style="color: #E5C07B">View </span><span style="color: #ABB2BF">{</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #7F848E; font-style: italic">// A @State variable, initialized to 0</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">@State</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">private</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">var</span><span style="color: #ABB2BF"> count = </span><span style="color: #D19A66">0</span></span><span class="line"></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">init</span><span style="color: #ABB2BF">() {</span></span><span class="line"><span style="color: #ABB2BF">        </span><span style="color: #7F848E; font-style: italic">// Log when this view is created</span></span><span class="line"><span style="color: #ABB2BF">        </span><span style="color: #56B6C2">print</span><span style="color: #ABB2BF">(</span><span style="color: #98C379">&quot;✅ CounterView has been initialized.&quot;</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #ABB2BF">    }</span></span><span class="line"></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">var</span><span style="color: #ABB2BF"> body: </span><span style="color: #C678DD">some</span><span style="color: #ABB2BF"> View {</span></span><span class="line"><span style="color: #ABB2BF">        </span><span style="color: #61AFEF">VStack</span><span style="color: #ABB2BF"> {</span></span><span class="line"><span style="color: #ABB2BF">            </span><span style="color: #61AFEF">Text</span><span style="color: #ABB2BF">(</span><span style="color: #98C379">&quot;Counter Value: </span><span style="color: #C678DD">\(</span><span style="color: #ABB2BF">count</span><span style="color: #C678DD">)</span><span style="color: #98C379">&quot;</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #ABB2BF">                .</span><span style="color: #61AFEF">font</span><span style="color: #ABB2BF">(.</span><span style="color: #E06C75">title</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #ABB2BF">                .</span><span style="color: #61AFEF">padding</span><span style="color: #ABB2BF">()</span></span><span class="line"><span style="color: #ABB2BF">            </span></span><span class="line"><span style="color: #ABB2BF">            </span><span style="color: #61AFEF">Button</span><span style="color: #ABB2BF">(</span><span style="color: #98C379">&quot;Increment Count&quot;</span><span style="color: #ABB2BF">) {</span></span><span class="line"><span style="color: #ABB2BF">                count += </span><span style="color: #D19A66">1</span></span><span class="line"><span style="color: #ABB2BF">            }</span></span><span class="line"><span style="color: #ABB2BF">        }</span></span><span class="line"><span style="color: #ABB2BF">        .</span><span style="color: #61AFEF">padding</span><span style="color: #ABB2BF">()</span></span><span class="line"><span style="color: #ABB2BF">        .</span><span style="color: #61AFEF">border</span><span style="color: #ABB2BF">(Color.</span><span style="color: #E06C75">blue</span><span style="color: #ABB2BF">, </span><span style="color: #61AFEF">width</span><span style="color: #ABB2BF">: </span><span style="color: #D19A66">2</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #ABB2BF">        </span><span style="color: #7F848E; font-style: italic">// This is called when the view is removed from the view tree</span></span><span class="line"><span style="color: #ABB2BF">        .</span><span style="color: #61AFEF">onDisappear</span><span style="color: #ABB2BF"> {</span></span><span class="line"><span style="color: #ABB2BF">            </span><span style="color: #56B6C2">print</span><span style="color: #ABB2BF">(</span><span style="color: #98C379">&quot;❌ CounterView has disappeared.&quot;</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #ABB2BF">        }</span></span><span class="line"><span style="color: #ABB2BF">    }</span></span><span class="line"><span style="color: #ABB2BF">}</span></span><span class="line"></span><span class="line"><span style="color: #C678DD">struct</span><span style="color: #ABB2BF"> </span><span style="color: #E5C07B">ContainerView</span><span style="color: #ABB2BF">: </span><span style="color: #E5C07B">View </span><span style="color: #ABB2BF">{</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">@State</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">private</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">var</span><span style="color: #ABB2BF"> showCounter = </span><span style="color: #D19A66">true</span></span><span class="line"></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">var</span><span style="color: #ABB2BF"> body: </span><span style="color: #C678DD">some</span><span style="color: #ABB2BF"> View {</span></span><span class="line"><span style="color: #ABB2BF">        </span><span style="color: #61AFEF">VStack</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">spacing</span><span style="color: #ABB2BF">: </span><span style="color: #D19A66">30</span><span style="color: #ABB2BF">) {</span></span><span class="line"><span style="color: #ABB2BF">            </span><span style="color: #61AFEF">Toggle</span><span style="color: #ABB2BF">(</span><span style="color: #98C379">&quot;Show/Hide Counter&quot;</span><span style="color: #ABB2BF">, </span><span style="color: #61AFEF">isOn</span><span style="color: #ABB2BF">: $showCounter.</span><span style="color: #61AFEF">animation</span><span style="color: #ABB2BF">())</span></span><span class="line"><span style="color: #ABB2BF">                .</span><span style="color: #61AFEF">padding</span><span style="color: #ABB2BF">()</span></span><span class="line"></span><span class="line"><span style="color: #ABB2BF">            </span><span style="color: #C678DD">if</span><span style="color: #ABB2BF"> showCounter {</span></span><span class="line"><span style="color: #ABB2BF">                </span><span style="color: #7F848E; font-style: italic">// When showCounter is true, CounterView exists in the view tree</span></span><span class="line"><span style="color: #ABB2BF">                </span><span style="color: #61AFEF">CounterView</span><span style="color: #ABB2BF">()</span></span><span class="line"><span style="color: #ABB2BF">            } </span><span style="color: #C678DD">else</span><span style="color: #ABB2BF"> {</span></span><span class="line"><span style="color: #ABB2BF">                </span><span style="color: #7F848E; font-style: italic">// When showCounter is false, CounterView is completely removed</span></span><span class="line"><span style="color: #ABB2BF">                </span><span style="color: #7F848E; font-style: italic">// A placeholder Text shows the structural change</span></span><span class="line"><span style="color: #ABB2BF">                </span><span style="color: #61AFEF">Text</span><span style="color: #ABB2BF">(</span><span style="color: #98C379">&quot;Counter is hidden&quot;</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #ABB2BF">                    .</span><span style="color: #61AFEF">foregroundColor</span><span style="color: #ABB2BF">(.</span><span style="color: #E06C75">gray</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #ABB2BF">            }</span></span><span class="line"><span style="color: #ABB2BF">            </span></span><span class="line"><span style="color: #ABB2BF">            </span><span style="color: #61AFEF">Spacer</span><span style="color: #ABB2BF">()</span></span><span class="line"><span style="color: #ABB2BF">        }</span></span><span class="line"><span style="color: #ABB2BF">        .</span><span style="color: #61AFEF">navigationTitle</span><span style="color: #ABB2BF">(</span><span style="color: #98C379">&quot;State Lifecycle Demo&quot;</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #ABB2BF">    }</span></span><span class="line"><span style="color: #ABB2BF">}</span></span></code></pre></div></div></figure><h4 id="How-to-Run-and-Observe"><a href="#How-to-Run-and-Observe" class="headerlink" title="How to Run and Observe"></a>How to Run and Observe</h4><ol><li>Run the <code>ContainerView</code>. You will see the counter, and the console will print: <strong>“✅ CounterView has been initialized.”</strong></li><li>Click the <strong>“Increment Count”</strong> button a few times to increase the count (e.g., to 5).</li><li>Now, tap the <strong>“Show&#x2F;Hide Counter”</strong> <code>Toggle</code> to turn it off.<ul><li>The <code>CounterView</code> will disappear from the screen.</li><li>The console will print: <strong>“❌ CounterView has disappeared.”</strong> This confirms the view instance was destroyed.</li></ul></li><li>Tap the <code>Toggle</code> again to turn it back on.<ul><li><code>CounterView</code> reappears on the screen.</li><li>The console will <strong>again</strong> print: <strong>“✅ CounterView has been initialized.”</strong> This proves that SwiftUI has created a <strong>brand new <code>CounterView</code> instance</strong>.</li><li>You will notice that the counter’s value has <strong>reset to 0</strong>, not the 5 you left it at.</li></ul></li></ol><p>This behavior perfectly illustrates that <code>@State</code>‘s storage is tied to the lifecycle of its containing view in the view hierarchy. If the view is removed, its state is lost. This is precisely the scenario where <code>@StateObject</code> should be used if you need the state to persist as long as the view’s identity remains the same.</p>]]></content>
    
    
      
      
    <summary type="html">&lt;h2 id=&quot;A-Comprehensive-Guide-to-State-Management-in-SwiftUI&quot;&gt;&lt;a href=&quot;#A-Comprehensive-Guide-to-State-Management-in-SwiftUI&quot; class=&quot;headerl</summary>
      
    
    
    
    <category term="Dev" scheme="https://ihugo.cc/categories/Dev/"/>
    
    
    <category term="iOS" scheme="https://ihugo.cc/tags/iOS/"/>
    
  </entry>
  
  <entry>
    <title>Minimalist Swift 6 Tutorial</title>
    <link href="https://ihugo.cc/2025/08/17/023-swift6-notes/"/>
    <id>https://ihugo.cc/2025/08/17/023-swift6-notes/</id>
    <published>2025-08-17T08:22:58.000Z</published>
    <updated>2026-05-25T03:24:31.410Z</updated>
    
    <content type="html"><![CDATA[<p>I am ready to systematically review the iOS knowledge, so I have this simple Swift 6 Programming study notes.</p><h2 id="Part-1-Core-Concepts"><a href="#Part-1-Core-Concepts" class="headerlink" title="Part 1: Core Concepts"></a>Part 1: Core Concepts</h2><h3 id="1-Value-vs-Reference-Types"><a href="#1-Value-vs-Reference-Types" class="headerlink" title="1. Value vs. Reference Types"></a>1. Value vs. Reference Types</h3><p>Types in Swift are divided into value types (<code>struct</code>, <code>enum</code>) and reference types (<code>class</code>). The fundamental difference lies in how their data is stored and passed.</p><ul><li><strong>Value Types</strong>: Each instance keeps a unique copy of its data. When you pass a value type, it is copied.</li><li><strong>Reference Types</strong>: Instances share a single copy of their data. When you pass a reference type, a reference (or pointer) to the instance is passed.</li></ul><p><strong>Copy-on-Write</strong></p><p>To optimize performance, many of Swift’s standard library value types (like Array and Dictionary) use a technique called Copy-on-Write. This means a copy is only made when the data needs to be modified; otherwise, multiple instances share the same data storage.</p><figure class="shiki swift"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"><span style="color: #C678DD">import</span><span style="color: #ABB2BF"> </span><span style="color: #E5C07B">Foundation</span></span><span class="line"></span><span class="line"><span style="color: #7F848E; font-style: italic">// A backing class for demonstration</span></span><span class="line"><span style="color: #C678DD">class</span><span style="color: #ABB2BF"> </span><span style="color: #E5C07B">BackendQueue</span><span style="color: #ABB2BF">&lt;</span><span style="color: #E5C07B">T</span><span style="color: #ABB2BF">&gt; {</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">private</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">var</span><span style="color: #ABB2BF"> items = [T]()</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">public</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">init</span><span style="color: #ABB2BF">() {}</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">private</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">init</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">_</span><span style="color: #ABB2BF"> </span><span style="color: #ABB2BF; font-style: italic">items</span><span style="color: #ABB2BF">: [T]) { </span><span style="color: #E5C07B">self</span><span style="color: #ABB2BF">.</span><span style="color: #E06C75">items</span><span style="color: #ABB2BF"> = items }</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">public</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">func</span><span style="color: #ABB2BF"> </span><span style="color: #61AFEF">addItem</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF; font-style: italic">item</span><span style="color: #ABB2BF">: T) { items.</span><span style="color: #56B6C2">append</span><span style="color: #ABB2BF">(item) }</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">public</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">func</span><span style="color: #ABB2BF"> </span><span style="color: #61AFEF">getItem</span><span style="color: #ABB2BF">() -&gt; T? {</span></span><span class="line"><span style="color: #ABB2BF">        </span><span style="color: #C678DD">if</span><span style="color: #ABB2BF"> items.count &gt; </span><span style="color: #D19A66">0</span><span style="color: #ABB2BF"> { </span><span style="color: #C678DD">return</span><span style="color: #ABB2BF"> items.</span><span style="color: #56B6C2">remove</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">at</span><span style="color: #ABB2BF">: </span><span style="color: #D19A66">0</span><span style="color: #ABB2BF">) } </span></span><span class="line"><span style="color: #ABB2BF">        </span><span style="color: #C678DD">else</span><span style="color: #ABB2BF"> { </span><span style="color: #C678DD">return</span><span style="color: #ABB2BF"> </span><span style="color: #D19A66">nil</span><span style="color: #ABB2BF"> }</span></span><span class="line"><span style="color: #ABB2BF">    }</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">public</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">func</span><span style="color: #ABB2BF"> </span><span style="color: #61AFEF">count</span><span style="color: #ABB2BF">() -&gt; </span><span style="color: #E5C07B">Int</span><span style="color: #ABB2BF"> { </span><span style="color: #C678DD">return</span><span style="color: #ABB2BF"> items.count }</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">public</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">func</span><span style="color: #ABB2BF"> </span><span style="color: #61AFEF">copy</span><span style="color: #ABB2BF">() -&gt; BackendQueue&lt;T&gt; { </span><span style="color: #C678DD">return</span><span style="color: #ABB2BF"> BackendQueue&lt;T&gt;(items) }</span></span><span class="line"><span style="color: #ABB2BF">}</span></span><span class="line"></span><span class="line"><span style="color: #7F848E; font-style: italic">// A struct wrapper to implement Copy-on-Write</span></span><span class="line"><span style="color: #C678DD">struct</span><span style="color: #ABB2BF"> </span><span style="color: #E5C07B">Queue</span><span style="color: #ABB2BF"> {</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">private</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">var</span><span style="color: #ABB2BF"> internalQueue = BackendQueue&lt;</span><span style="color: #E5C07B">Int</span><span style="color: #ABB2BF">&gt;()</span></span><span class="line"><span style="color: #ABB2BF">    </span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">public</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">mutating</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">func</span><span style="color: #ABB2BF"> </span><span style="color: #61AFEF">addItem</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF; font-style: italic">item</span><span style="color: #ABB2BF">: </span><span style="color: #E5C07B">Int</span><span style="color: #ABB2BF">) {</span></span><span class="line"><span style="color: #ABB2BF">        </span><span style="color: #61AFEF">checkUniquelyReferencedInternalQueue</span><span style="color: #ABB2BF">()</span></span><span class="line"><span style="color: #ABB2BF">        internalQueue.</span><span style="color: #61AFEF">addItem</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">item</span><span style="color: #ABB2BF">: item)</span></span><span class="line"><span style="color: #ABB2BF">    }</span></span><span class="line"><span style="color: #ABB2BF">    </span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">public</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">func</span><span style="color: #ABB2BF"> </span><span style="color: #61AFEF">count</span><span style="color: #ABB2BF">() -&gt; </span><span style="color: #E5C07B">Int</span><span style="color: #ABB2BF"> {</span></span><span class="line"><span style="color: #ABB2BF">        </span><span style="color: #C678DD">return</span><span style="color: #ABB2BF"> internalQueue.count()</span></span><span class="line"><span style="color: #ABB2BF">    }</span></span><span class="line"><span style="color: #ABB2BF">    </span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">mutating</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">private</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">func</span><span style="color: #ABB2BF"> </span><span style="color: #61AFEF">checkUniquelyReferencedInternalQueue</span><span style="color: #ABB2BF">() {</span></span><span class="line"><span style="color: #ABB2BF">        </span><span style="color: #C678DD">if</span><span style="color: #ABB2BF"> !</span><span style="color: #56B6C2">isKnownUniquelyReferenced</span><span style="color: #ABB2BF">(&amp;internalQueue) {</span></span><span class="line"><span style="color: #ABB2BF">            internalQueue = internalQueue.</span><span style="color: #56B6C2">copy</span><span style="color: #ABB2BF">()</span></span><span class="line"><span style="color: #ABB2BF">            </span><span style="color: #56B6C2">print</span><span style="color: #ABB2BF">(</span><span style="color: #98C379">&quot;Making a copy of internalQueue&quot;</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #ABB2BF">        } </span><span style="color: #C678DD">else</span><span style="color: #ABB2BF"> {</span></span><span class="line"><span style="color: #ABB2BF">            </span><span style="color: #56B6C2">print</span><span style="color: #ABB2BF">(</span><span style="color: #98C379">&quot;Not making a copy of internalQueue&quot;</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #ABB2BF">        }</span></span><span class="line"><span style="color: #ABB2BF">    }</span></span><span class="line"><span style="color: #ABB2BF">}</span></span><span class="line"></span><span class="line"><span style="color: #56B6C2">print</span><span style="color: #ABB2BF">(</span><span style="color: #98C379">&quot;Start&quot;</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #C678DD">var</span><span style="color: #ABB2BF"> queue1 = </span><span style="color: #61AFEF">Queue</span><span style="color: #ABB2BF">()</span></span><span class="line"><span style="color: #C678DD">var</span><span style="color: #ABB2BF"> queue2 = queue1 </span><span style="color: #7F848E; font-style: italic">// No copy happens here</span></span><span class="line"></span><span class="line"><span style="color: #56B6C2">print</span><span style="color: #ABB2BF">(</span><span style="color: #98C379">&quot;ADD&quot;</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #ABB2BF">queue1.</span><span style="color: #61AFEF">addItem</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">item</span><span style="color: #ABB2BF">: </span><span style="color: #D19A66">1</span><span style="color: #ABB2BF">) </span><span style="color: #7F848E; font-style: italic">// Modifying queue1 triggers a copy</span></span><span class="line"><span style="color: #ABB2BF">queue1.</span><span style="color: #61AFEF">addItem</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">item</span><span style="color: #ABB2BF">: </span><span style="color: #D19A66">2</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #56B6C2">print</span><span style="color: #ABB2BF">(queue1.count())</span></span><span class="line"><span style="color: #56B6C2">print</span><span style="color: #ABB2BF">(</span><span style="color: #98C379">&quot;Done&quot;</span><span style="color: #ABB2BF">)</span></span><span class="line"></span><span class="line"><span style="color: #7F848E; font-style: italic">/*</span></span><span class="line"><span style="color: #7F848E; font-style: italic">Console Output:</span></span><span class="line"><span style="color: #7F848E; font-style: italic">Start</span></span><span class="line"><span style="color: #7F848E; font-style: italic">ADD</span></span><span class="line"><span style="color: #7F848E; font-style: italic">Making a copy of internalQueue</span></span><span class="line"><span style="color: #7F848E; font-style: italic">Not making a copy of internalQueue</span></span><span class="line"><span style="color: #7F848E; font-style: italic">2</span></span><span class="line"><span style="color: #7F848E; font-style: italic">Done</span></span><span class="line"><span style="color: #7F848E; font-style: italic">*/</span></span></code></pre></div></div></figure><h4 id="Noncopyable-Types-Copyable"><a href="#Noncopyable-Types-Copyable" class="headerlink" title="Noncopyable Types (~Copyable)"></a>Noncopyable Types (<code>~Copyable</code>)</h4><p>Swift 6 introduces noncopyable types to represent unique resources like file handles or network sockets, ensuring they are not accidentally duplicated.</p><figure class="shiki swift"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"><span style="color: #C678DD">struct</span><span style="color: #ABB2BF"> </span><span style="color: #E5C07B">Person</span><span style="color: #ABB2BF">: ~</span><span style="color: #E5C07B">Copyable </span><span style="color: #ABB2BF">{</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">var</span><span style="color: #ABB2BF"> firstName: </span><span style="color: #E5C07B">String</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">var</span><span style="color: #ABB2BF"> lastName: </span><span style="color: #E5C07B">String</span></span><span class="line"><span style="color: #ABB2BF">}</span></span></code></pre></div></div></figure><p>Two key concepts related to noncopyable types are <strong>borrowing</strong> and <strong>consuming</strong>.</p><ul><li><strong>Borrowing (<code>borrowing</code>)</strong>: Grants temporary, read-only access to a noncopyable value without transferring ownership. Borrowed values are thread-safe.</li><li><strong>Consuming (<code>consuming</code>)</strong>: Transfers ownership of a noncopyable value, and the original variable becomes invalid. A consuming method ends the object’s lifetime upon its return. Global instances cannot be consumed.</li></ul><!-- end list --><figure class="shiki swift"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"><span style="color: #7F848E; font-style: italic">// The `borrowing` keyword indicates the function temporarily borrows `user` without taking ownership</span></span><span class="line"><span style="color: #C678DD">func</span><span style="color: #ABB2BF"> </span><span style="color: #61AFEF">sendEmail</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">_</span><span style="color: #ABB2BF"> </span><span style="color: #ABB2BF; font-style: italic">user</span><span style="color: #ABB2BF">: </span><span style="color: #C678DD">borrowing</span><span style="color: #ABB2BF"> Person) {</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #56B6C2">print</span><span style="color: #ABB2BF">(</span><span style="color: #98C379">&quot;Sending Email to </span><span style="color: #C678DD">\(</span><span style="color: #ABB2BF">user.</span><span style="color: #E06C75">firstName</span><span style="color: #C678DD">)</span><span style="color: #98C379">&quot;</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #ABB2BF">}</span></span><span class="line"></span><span class="line"><span style="color: #7F848E; font-style: italic">// The `consuming` keyword indicates the function consumes `user`, taking ownership</span></span><span class="line"><span style="color: #C678DD">func</span><span style="color: #ABB2BF"> </span><span style="color: #61AFEF">consumeUser</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">_</span><span style="color: #ABB2BF"> </span><span style="color: #ABB2BF; font-style: italic">user</span><span style="color: #ABB2BF">: </span><span style="color: #C678DD">consuming</span><span style="color: #ABB2BF"> Person) {</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #56B6C2">print</span><span style="color: #ABB2BF">(</span><span style="color: #98C379">&quot;Consuming User </span><span style="color: #C678DD">\(</span><span style="color: #ABB2BF">user.</span><span style="color: #E06C75">firstName</span><span style="color: #C678DD">)</span><span style="color: #98C379">&quot;</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #ABB2BF">}</span></span><span class="line"></span><span class="line"><span style="color: #C678DD">func</span><span style="color: #ABB2BF"> </span><span style="color: #61AFEF">userFunction</span><span style="color: #ABB2BF">() {</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> user = </span><span style="color: #61AFEF">Person</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">firstName</span><span style="color: #ABB2BF">: </span><span style="color: #98C379">&quot;Jon&quot;</span><span style="color: #ABB2BF">, </span><span style="color: #61AFEF">lastName</span><span style="color: #ABB2BF">: </span><span style="color: #98C379">&quot;Hoffman&quot;</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #61AFEF">sendEmail</span><span style="color: #ABB2BF">(user) </span><span style="color: #7F848E; font-style: italic">// user is borrowed</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #61AFEF">consumeUser</span><span style="color: #ABB2BF">(user) </span><span style="color: #7F848E; font-style: italic">// user is consumed, and the variable becomes invalid afterward</span></span><span class="line"><span style="color: #ABB2BF">}</span></span></code></pre></div></div></figure><p>We can also create consuming methods that invalidate the instance once executed.</p><figure class="shiki swift"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"><span style="color: #C678DD">struct</span><span style="color: #ABB2BF"> </span><span style="color: #E5C07B">SecrectMessage</span><span style="color: #ABB2BF">: ~</span><span style="color: #E5C07B">Copyable </span><span style="color: #ABB2BF">{</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">private</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">var</span><span style="color: #ABB2BF"> message: </span><span style="color: #E5C07B">String</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">init</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">_</span><span style="color: #ABB2BF"> </span><span style="color: #ABB2BF; font-style: italic">message</span><span style="color: #ABB2BF">: </span><span style="color: #E5C07B">String</span><span style="color: #ABB2BF">) { </span><span style="color: #E5C07B">self</span><span style="color: #ABB2BF">.</span><span style="color: #E06C75">message</span><span style="color: #ABB2BF"> = message }</span></span><span class="line"><span style="color: #ABB2BF">    </span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #7F848E; font-style: italic">// After this consuming method is executed, the instance is destroyed</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">consuming</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">func</span><span style="color: #ABB2BF"> </span><span style="color: #61AFEF">read</span><span style="color: #ABB2BF">() {</span></span><span class="line"><span style="color: #ABB2BF">        </span><span style="color: #56B6C2">print</span><span style="color: #ABB2BF">(</span><span style="color: #98C379">&quot;</span><span style="color: #C678DD">\(</span><span style="color: #ABB2BF">message</span><span style="color: #C678DD">)</span><span style="color: #98C379">&quot;</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #ABB2BF">    }</span></span><span class="line"><span style="color: #ABB2BF">}</span></span></code></pre></div></div></figure><h3 id="2-Enumerations"><a href="#2-Enumerations" class="headerlink" title="2. Enumerations"></a>2. Enumerations</h3><p>Enumerations define a common type for a group of related values.</p><ul><li><p><strong>Raw Values</strong>: Enum members can be prepopulated with a default value.</p><figure class="shiki swift"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"><span style="color: #C678DD">enum</span><span style="color: #ABB2BF"> </span><span style="color: #E5C07B">Direction</span><span style="color: #ABB2BF">: </span><span style="color: #E5C07B">String </span><span style="color: #ABB2BF">{</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">case</span><span style="color: #ABB2BF"> </span><span style="color: #E06C75">North</span><span style="color: #ABB2BF"> </span><span style="color: #56B6C2">=</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">&quot;N&quot;</span><span style="color: #ABB2BF">, </span><span style="color: #E06C75">South</span><span style="color: #ABB2BF"> </span><span style="color: #56B6C2">=</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">&quot;S&quot;</span><span style="color: #ABB2BF">, </span><span style="color: #E06C75">West</span><span style="color: #ABB2BF"> </span><span style="color: #56B6C2">=</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">&quot;W&quot;</span><span style="color: #ABB2BF">, </span><span style="color: #E06C75">East</span><span style="color: #ABB2BF"> </span><span style="color: #56B6C2">=</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">&quot;E&quot;</span></span><span class="line"><span style="color: #ABB2BF">}</span></span><span class="line"><span style="color: #C678DD">enum</span><span style="color: #ABB2BF"> </span><span style="color: #E5C07B">Month</span><span style="color: #ABB2BF">: </span><span style="color: #E5C07B">Int </span><span style="color: #ABB2BF">{</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">case</span><span style="color: #ABB2BF"> </span><span style="color: #E06C75">January</span><span style="color: #ABB2BF"> </span><span style="color: #56B6C2">=</span><span style="color: #ABB2BF"> </span><span style="color: #D19A66">1</span><span style="color: #ABB2BF">, </span><span style="color: #E06C75">February</span><span style="color: #ABB2BF">, </span><span style="color: #E06C75">March</span><span style="color: #ABB2BF">, </span><span style="color: #E06C75">April</span><span style="color: #ABB2BF"> </span><span style="color: #7F848E; font-style: italic">// 2, 3, 4 are inferred automatically</span></span><span class="line"><span style="color: #ABB2BF">}</span></span></code></pre></div></div></figure></li><li><p><strong>Associated Values</strong>: Store custom values associated with an enum member.</p><figure class="shiki swift"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"><span style="color: #C678DD">enum</span><span style="color: #ABB2BF"> </span><span style="color: #E5C07B">Product</span><span style="color: #ABB2BF"> {</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">case</span><span style="color: #ABB2BF"> </span><span style="color: #E06C75">Book</span><span style="color: #ABB2BF">(</span><span style="color: #E5C07B">Double</span><span style="color: #ABB2BF">, </span><span style="color: #E5C07B">Int</span><span style="color: #ABB2BF">, </span><span style="color: #E5C07B">Int</span><span style="color: #ABB2BF">) </span><span style="color: #7F848E; font-style: italic">// price, year, pages</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">case</span><span style="color: #ABB2BF"> </span><span style="color: #E06C75">Puzzle</span><span style="color: #ABB2BF">(</span><span style="color: #E5C07B">Double</span><span style="color: #ABB2BF">, </span><span style="color: #E5C07B">Int</span><span style="color: #ABB2BF">)   </span><span style="color: #7F848E; font-style: italic">// price, pieces</span></span><span class="line"><span style="color: #ABB2BF">}</span></span></code></pre></div></div></figure></li><li><p><strong>Pattern Matching</strong>: The <code>switch</code> statement makes it easy to handle different enum cases and extract their associated values.</p><figure class="shiki swift"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> masterSwift = Product.</span><span style="color: #61AFEF">Book</span><span style="color: #ABB2BF">(</span><span style="color: #D19A66">49.99</span><span style="color: #ABB2BF">, </span><span style="color: #D19A66">2024</span><span style="color: #ABB2BF">, </span><span style="color: #D19A66">394</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #C678DD">switch</span><span style="color: #ABB2BF"> masterSwift {</span></span><span class="line"><span style="color: #C678DD">case</span><span style="color: #ABB2BF"> .</span><span style="color: #61AFEF">Book</span><span style="color: #ABB2BF">(</span><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> price, </span><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> year, </span><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> pages)</span><span style="color: #C678DD">:</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #56B6C2">print</span><span style="color: #ABB2BF">(</span><span style="color: #98C379">&quot;Mastering Swift was published in </span><span style="color: #C678DD">\(</span><span style="color: #ABB2BF">year</span><span style="color: #C678DD">)</span><span style="color: #98C379"> for </span><span style="color: #C678DD">\(</span><span style="color: #ABB2BF">price</span><span style="color: #C678DD">)</span><span style="color: #98C379"> and has </span><span style="color: #C678DD">\(</span><span style="color: #ABB2BF">pages</span><span style="color: #C678DD">)</span><span style="color: #98C379"> pages&quot;</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #C678DD">case</span><span style="color: #ABB2BF"> .</span><span style="color: #61AFEF">Puzzle</span><span style="color: #ABB2BF">(</span><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> price, </span><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> pieces)</span><span style="color: #C678DD">:</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #56B6C2">print</span><span style="color: #ABB2BF">(</span><span style="color: #98C379">&quot;A puzzle with </span><span style="color: #C678DD">\(</span><span style="color: #ABB2BF">pieces</span><span style="color: #C678DD">)</span><span style="color: #98C379"> pieces and sells for </span><span style="color: #C678DD">\(</span><span style="color: #ABB2BF">price</span><span style="color: #C678DD">)</span><span style="color: #98C379">&quot;</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #ABB2BF">}</span></span></code></pre></div></div></figure></li><li><p><strong>Enum Iteration</strong>: By conforming to the <code>CaseIterable</code> protocol, you can iterate over all members of an enumeration.</p><figure class="shiki swift"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"><span style="color: #C678DD">enum</span><span style="color: #ABB2BF"> </span><span style="color: #E5C07B">DaysOfWeek</span><span style="color: #ABB2BF">: </span><span style="color: #E5C07B">String</span><span style="color: #ABB2BF">, </span><span style="color: #E5C07B">CaseIterable </span><span style="color: #ABB2BF">{</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">case</span><span style="color: #ABB2BF"> </span><span style="color: #E06C75">Monday</span><span style="color: #ABB2BF"> </span><span style="color: #56B6C2">=</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">&quot;Mon&quot;</span><span style="color: #ABB2BF">, </span><span style="color: #E06C75">Tuesday</span><span style="color: #ABB2BF"> </span><span style="color: #56B6C2">=</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">&quot;Tues&quot;</span><span style="color: #ABB2BF">, </span><span style="color: #E06C75">Wednesday</span><span style="color: #ABB2BF"> </span><span style="color: #56B6C2">=</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">&quot;Wed&quot;</span><span style="color: #ABB2BF">, </span><span style="color: #E06C75">Thursday</span><span style="color: #ABB2BF"> </span><span style="color: #56B6C2">=</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">&quot;Thur&quot;</span><span style="color: #ABB2BF">, </span><span style="color: #E06C75">Friday</span><span style="color: #ABB2BF"> </span><span style="color: #56B6C2">=</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">&quot;Fri&quot;</span><span style="color: #ABB2BF">, </span><span style="color: #E06C75">Saturday</span><span style="color: #ABB2BF"> </span><span style="color: #56B6C2">=</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">&quot;Sat&quot;</span><span style="color: #ABB2BF">, </span><span style="color: #E06C75">Sunday</span><span style="color: #ABB2BF"> </span><span style="color: #56B6C2">=</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">&quot;Sun&quot;</span></span><span class="line"><span style="color: #ABB2BF">}</span></span><span class="line"></span><span class="line"><span style="color: #C678DD">for</span><span style="color: #ABB2BF"> day </span><span style="color: #C678DD">in</span><span style="color: #ABB2BF"> DaysOfWeek.allCases {</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #56B6C2">print</span><span style="color: #ABB2BF">(</span><span style="color: #98C379">&quot;-- </span><span style="color: #C678DD">\(</span><span style="color: #ABB2BF">day.rawValue</span><span style="color: #C678DD">)</span><span style="color: #98C379">&quot;</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #ABB2BF">}</span></span></code></pre></div></div></figure></li></ul><h3 id="3-Closures"><a href="#3-Closures" class="headerlink" title="3. Closures"></a>3. Closures</h3><p>Closures are self-contained blocks of functionality that can be passed around and used in your code.</p><ul><li><p><strong>Basic Syntax</strong>:</p><figure class="shiki swift"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> clos1 = { () -&gt; </span><span style="color: #E5C07B">Void</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">in</span></span><span class="line"><span style="color: #ABB2BF">  </span><span style="color: #56B6C2">print</span><span style="color: #ABB2BF">(</span><span style="color: #98C379">&quot;hello world&quot;</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #ABB2BF">}</span></span><span class="line"><span style="color: #61AFEF">clos1</span><span style="color: #ABB2BF">()</span></span></code></pre></div></div></figure></li><li><p><strong>Shorthand Syntax</strong>: Swift provides several ways to simplify closure syntax.</p><figure class="shiki swift"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"><span style="color: #7F848E; font-style: italic">// $0, $1 represent the first and second parameters</span></span><span class="line"><span style="color: #ABB2BF">guests.</span><span style="color: #56B6C2">map</span><span style="color: #ABB2BF"> { </span><span style="color: #56B6C2">print</span><span style="color: #ABB2BF">(</span><span style="color: #98C379">&quot;hello </span><span style="color: #C678DD">\(</span><span style="color: #E5C07B">$0</span><span style="color: #C678DD">)</span><span style="color: #98C379">&quot;</span><span style="color: #ABB2BF">) }</span></span><span class="line"></span><span class="line"><span style="color: #7F848E; font-style: italic">// If the closure is the only argument to a function, the parentheses can be omitted</span></span><span class="line"><span style="color: #61AFEF">testFunction2</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">num</span><span style="color: #ABB2BF">: </span><span style="color: #D19A66">5</span><span style="color: #ABB2BF">) {</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #56B6C2">print</span><span style="color: #ABB2BF">(</span><span style="color: #98C379">&quot;hello from </span><span style="color: #C678DD">\(</span><span style="color: #E5C07B">$0</span><span style="color: #C678DD">)</span><span style="color: #98C379">&quot;</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #ABB2BF">}</span></span><span class="line"></span><span class="line"><span style="color: #7F848E; font-style: italic">// Single-expression closures can implicitly return their result</span></span><span class="line"><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> clos7 = { (</span><span style="color: #61AFEF">first</span><span style="color: #ABB2BF">: </span><span style="color: #E5C07B">Int</span><span style="color: #ABB2BF">, </span><span style="color: #61AFEF">second</span><span style="color: #ABB2BF">: </span><span style="color: #E5C07B">Int</span><span style="color: #ABB2BF">) -&gt; </span><span style="color: #E5C07B">Int</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">in</span><span style="color: #ABB2BF"> first + second }</span></span><span class="line"><span style="color: #56B6C2">print</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">clos7</span><span style="color: #ABB2BF">(</span><span style="color: #D19A66">1</span><span style="color: #ABB2BF">, </span><span style="color: #D19A66">2</span><span style="color: #ABB2BF">)) </span><span style="color: #7F848E; font-style: italic">// Prints 3</span></span></code></pre></div></div></figure></li><li><p><strong>Escaping Closures (<code>@escaping</code>)</strong>: When a closure is called after the function it was passed to returns, it needs to be marked with the <code>@escaping</code> keyword. This typically happens when the closure is stored for later use or executed in an asynchronous operation.</p><figure class="shiki swift"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"><span style="color: #C678DD">var</span><span style="color: #ABB2BF"> handlers: [LogLevel: [logLevelHandler]] = [</span><span style="color: #C678DD">:</span><span style="color: #ABB2BF">]</span></span><span class="line"><span style="color: #C678DD">func</span><span style="color: #ABB2BF"> </span><span style="color: #61AFEF">registerHandler</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">for</span><span style="color: #ABB2BF"> </span><span style="color: #ABB2BF; font-style: italic">level</span><span style="color: #ABB2BF">: LogLevel, </span><span style="color: #61AFEF; font-style: italic">handler</span><span style="color: #ABB2BF">: </span><span style="color: #C678DD">@escaping</span><span style="color: #ABB2BF"> logLevelHandler) {</span></span><span class="line"><span style="color: #ABB2BF">    handlers[level, </span><span style="color: #61AFEF">default</span><span style="color: #ABB2BF">: []].</span><span style="color: #56B6C2">append</span><span style="color: #ABB2BF">(handler)</span></span><span class="line"><span style="color: #ABB2BF">}</span></span></code></pre></div></div></figure></li></ul><h3 id="4-Error-Handling"><a href="#4-Error-Handling" class="headerlink" title="4. Error Handling"></a>4. Error Handling</h3><p>Swift provides a powerful error handling model that allows you to represent and respond to recoverable errors.</p><ul><li><p><strong>Defining Errors</strong>: Create an enum that conforms to the <code>Error</code> protocol to represent different kinds of errors.</p><figure class="shiki swift"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"><span style="color: #C678DD">enum</span><span style="color: #ABB2BF"> </span><span style="color: #E5C07B">PlayerNumberError</span><span style="color: #ABB2BF">: </span><span style="color: #E5C07B">Error </span><span style="color: #ABB2BF">{</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">case</span><span style="color: #ABB2BF"> </span><span style="color: #E06C75">NumberTooHigh</span><span style="color: #ABB2BF">(</span><span style="color: #ABB2BF; font-style: italic">description</span><span style="color: #ABB2BF">: </span><span style="color: #E5C07B">String</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">case</span><span style="color: #ABB2BF"> </span><span style="color: #E06C75">NumberTooLow</span><span style="color: #ABB2BF">(</span><span style="color: #ABB2BF; font-style: italic">description</span><span style="color: #ABB2BF">: </span><span style="color: #E5C07B">String</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">case</span><span style="color: #ABB2BF"> </span><span style="color: #E06C75">NumberAlreadyAssigned</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">case</span><span style="color: #ABB2BF"> </span><span style="color: #E06C75">NumberDoesNotExist</span></span><span class="line"><span style="color: #ABB2BF">}</span></span></code></pre></div></div></figure></li><li><p><strong>Throwing Errors (<code>throws</code>)</strong>: Use the <code>throw</code> keyword to throw an error within a function. The function’s signature must be marked with <code>throws</code>.</p><figure class="shiki swift"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"><span style="color: #C678DD">mutating</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">func</span><span style="color: #ABB2BF"> </span><span style="color: #61AFEF">addPlayer</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF; font-style: italic">player</span><span style="color: #ABB2BF">: BaseballPlayer) </span><span style="color: #C678DD">throws</span><span style="color: #ABB2BF"> {</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">guard</span><span style="color: #ABB2BF"> player.number &lt; maxNumber </span><span style="color: #C678DD">else</span><span style="color: #ABB2BF"> {</span></span><span class="line"><span style="color: #ABB2BF">        </span><span style="color: #C678DD">throw</span><span style="color: #ABB2BF"> PlayerNumberError.</span><span style="color: #61AFEF">NumberTooHigh</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">description</span><span style="color: #ABB2BF">: </span><span style="color: #98C379">&quot;Max number is </span><span style="color: #C678DD">\(</span><span style="color: #ABB2BF">maxNumber</span><span style="color: #C678DD">)</span><span style="color: #98C379">&quot;</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #ABB2BF">    }</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #7F848E; font-style: italic">// ... other checks</span></span><span class="line"><span style="color: #ABB2BF">}</span></span></code></pre></div></div></figure></li><li><p><strong>Catching Errors (<code>do-catch</code>)</strong>: Use a <code>do-catch</code> statement to call a function that can throw an error.</p><figure class="shiki swift"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"><span style="color: #C678DD">do</span><span style="color: #ABB2BF"> {</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> player = </span><span style="color: #C678DD">try</span><span style="color: #ABB2BF"> myTeam.</span><span style="color: #61AFEF">getPlayerByNumber</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">number</span><span style="color: #ABB2BF">: </span><span style="color: #D19A66">34</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #56B6C2">print</span><span style="color: #ABB2BF">(</span><span style="color: #98C379">&quot;Player is </span><span style="color: #C678DD">\(</span><span style="color: #ABB2BF">player.</span><span style="color: #E06C75">firstName</span><span style="color: #C678DD">)</span><span style="color: #98C379">&quot;</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #ABB2BF">} </span><span style="color: #C678DD">catch</span><span style="color: #ABB2BF"> PlayerNumberError.</span><span style="color: #61AFEF">NumberDoesNotExist</span><span style="color: #ABB2BF"> {</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #56B6C2">print</span><span style="color: #ABB2BF">(</span><span style="color: #98C379">&quot;No player has that number&quot;</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #ABB2BF">} </span><span style="color: #C678DD">catch</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> </span><span style="color: #61AFEF">error</span><span style="color: #ABB2BF"> { </span><span style="color: #7F848E; font-style: italic">// Catch all other errors</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #56B6C2">print</span><span style="color: #ABB2BF">(</span><span style="color: #98C379">&quot;An error occurred: </span><span style="color: #C678DD">\(</span><span style="color: #ABB2BF">error</span><span style="color: #C678DD">)</span><span style="color: #98C379">&quot;</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #ABB2BF">}</span></span></code></pre></div></div></figure><p>You can also match multiple error patterns in a single <code>catch</code> clause:</p><figure class="shiki swift"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"><span style="color: #C678DD">catch</span><span style="color: #ABB2BF"> PlayerNumberError.</span><span style="color: #E06C75">NumberTooHigh</span><span style="color: #ABB2BF">, PlayerNumberError.</span><span style="color: #61AFEF">NumberTooLow</span><span style="color: #ABB2BF"> {</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #56B6C2">print</span><span style="color: #ABB2BF">(</span><span style="color: #98C379">&quot;Number is out of range.&quot;</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #ABB2BF">}</span></span></code></pre></div></div></figure></li><li><p><strong><code>LocalizedError</code> Protocol</strong>: Conforming to this protocol can provide richer, localized descriptions for your errors.</p><figure class="shiki swift"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"><span style="color: #C678DD">enum</span><span style="color: #ABB2BF"> </span><span style="color: #E5C07B">PlayerNumberError</span><span style="color: #ABB2BF">: </span><span style="color: #E5C07B">Error</span><span style="color: #ABB2BF">, </span><span style="color: #E5C07B">LocalizedError </span><span style="color: #ABB2BF">{</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #7F848E; font-style: italic">// ... cases</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">var</span><span style="color: #ABB2BF"> errorDescription: </span><span style="color: #E5C07B">String</span><span style="color: #ABB2BF">? {</span></span><span class="line"><span style="color: #ABB2BF">        </span><span style="color: #C678DD">switch</span><span style="color: #ABB2BF"> </span><span style="color: #E5C07B">self</span><span style="color: #ABB2BF"> {</span></span><span class="line"><span style="color: #ABB2BF">        </span><span style="color: #C678DD">case</span><span style="color: #ABB2BF"> .</span><span style="color: #E06C75">NumberAlreadyAssigned</span><span style="color: #C678DD">:</span></span><span class="line"><span style="color: #ABB2BF">            </span><span style="color: #C678DD">return</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">&quot;Player number already assigned&quot;</span></span><span class="line"><span style="color: #ABB2BF">        </span><span style="color: #7F848E; font-style: italic">// ... other descriptions</span></span><span class="line"><span style="color: #ABB2BF">        }</span></span><span class="line"><span style="color: #ABB2BF">    }</span></span><span class="line"><span style="color: #ABB2BF">}</span></span></code></pre></div></div></figure></li><li><p><strong>Defer Statement (<code>defer</code>)</strong>: The code within a <code>defer</code> block is executed just before the current scope is exited, whether by normal completion or by throwing an error. This is very useful for resource cleanup.</p><figure class="shiki swift"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"><span style="color: #C678DD">func</span><span style="color: #ABB2BF"> </span><span style="color: #61AFEF">processFile</span><span style="color: #ABB2BF">() {</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> file = </span><span style="color: #61AFEF">openFile</span><span style="color: #ABB2BF">()</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">defer</span><span style="color: #ABB2BF"> {</span></span><span class="line"><span style="color: #ABB2BF">        </span><span style="color: #61AFEF">closeFile</span><span style="color: #ABB2BF">(file) </span><span style="color: #7F848E; font-style: italic">// Ensures the file is always closed</span></span><span class="line"><span style="color: #ABB2BF">        </span><span style="color: #56B6C2">print</span><span style="color: #ABB2BF">(</span><span style="color: #98C379">&quot;File closed.&quot;</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #ABB2BF">    }</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #7F848E; font-style: italic">// ... file processing code that might throw an error</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #56B6C2">print</span><span style="color: #ABB2BF">(</span><span style="color: #98C379">&quot;File processed.&quot;</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #ABB2BF">}</span></span></code></pre></div></div></figure></li></ul><h3 id="5-Memory-Management"><a href="#5-Memory-Management" class="headerlink" title="5. Memory Management"></a>5. Memory Management</h3><p>Swift uses <strong>Automatic Reference Counting (ARC)</strong> to manage memory. ARC tracks the number of references to class instances. When the reference count for an instance drops to zero, the instance is deallocated, and its memory is freed.</p><figure class="shiki swift"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"><span style="color: #C678DD">class</span><span style="color: #ABB2BF"> </span><span style="color: #E5C07B">MyClass</span><span style="color: #ABB2BF"> {</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">var</span><span style="color: #ABB2BF"> name: </span><span style="color: #E5C07B">String</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">init</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF; font-style: italic">name</span><span style="color: #ABB2BF">: </span><span style="color: #E5C07B">String</span><span style="color: #ABB2BF">) { </span><span style="color: #E5C07B">self</span><span style="color: #ABB2BF">.</span><span style="color: #E06C75">name</span><span style="color: #ABB2BF"> = name; </span><span style="color: #56B6C2">print</span><span style="color: #ABB2BF">(</span><span style="color: #98C379">&quot;Initializing </span><span style="color: #C678DD">\(</span><span style="color: #ABB2BF">name</span><span style="color: #C678DD">)</span><span style="color: #98C379">&quot;</span><span style="color: #ABB2BF">) }</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">deinit</span><span style="color: #ABB2BF"> { </span><span style="color: #56B6C2">print</span><span style="color: #ABB2BF">(</span><span style="color: #98C379">&quot;Releasing </span><span style="color: #C678DD">\(</span><span style="color: #ABB2BF">name</span><span style="color: #C678DD">)</span><span style="color: #98C379">&quot;</span><span style="color: #ABB2BF">) }</span></span><span class="line"><span style="color: #ABB2BF">}</span></span><span class="line"></span><span class="line"><span style="color: #C678DD">var</span><span style="color: #ABB2BF"> ref1: MyClass? = </span><span style="color: #61AFEF">MyClass</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">name</span><span style="color: #ABB2BF">: </span><span style="color: #98C379">&quot;One&quot;</span><span style="color: #ABB2BF">) </span><span style="color: #7F848E; font-style: italic">// Reference count is 1</span></span><span class="line"><span style="color: #C678DD">var</span><span style="color: #ABB2BF"> ref2: MyClass? = ref1               </span><span style="color: #7F848E; font-style: italic">// Reference count is 2</span></span><span class="line"><span style="color: #ABB2BF">ref1 = </span><span style="color: #D19A66">nil</span><span style="color: #ABB2BF">                              </span><span style="color: #7F848E; font-style: italic">// Reference count is 1</span></span><span class="line"><span style="color: #ABB2BF">ref2 = </span><span style="color: #D19A66">nil</span><span style="color: #ABB2BF">                              </span><span style="color: #7F848E; font-style: italic">// Reference count is 0, instance is deallocated</span></span></code></pre></div></div></figure><h4 id="Strong-Reference-Cycles"><a href="#Strong-Reference-Cycles" class="headerlink" title="Strong Reference Cycles"></a>Strong Reference Cycles</h4><p>If two class instances hold a strong reference to each other, they will never be deallocated, causing a memory leak.</p><figure class="shiki swift"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"><span style="color: #C678DD">class</span><span style="color: #ABB2BF"> </span><span style="color: #E5C07B">MyClass1_Strong</span><span style="color: #ABB2BF"> {</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">var</span><span style="color: #ABB2BF"> class2: MyClass2_Strong?</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #7F848E; font-style: italic">// ...</span></span><span class="line"><span style="color: #ABB2BF">}</span></span><span class="line"><span style="color: #C678DD">class</span><span style="color: #ABB2BF"> </span><span style="color: #E5C07B">MyClass2_Strong</span><span style="color: #ABB2BF"> {</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">var</span><span style="color: #ABB2BF"> class1: MyClass1_Strong?</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #7F848E; font-style: italic">// ...</span></span><span class="line"><span style="color: #ABB2BF">}</span></span><span class="line"></span><span class="line"><span style="color: #C678DD">var</span><span style="color: #ABB2BF"> class1: MyClass1_Strong? = </span><span style="color: #61AFEF">MyClass1_Strong</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">name</span><span style="color: #ABB2BF">: </span><span style="color: #98C379">&quot;Class1&quot;</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #C678DD">var</span><span style="color: #ABB2BF"> class2: MyClass2_Strong? = </span><span style="color: #61AFEF">MyClass2_Strong</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">name</span><span style="color: #ABB2BF">: </span><span style="color: #98C379">&quot;Class2&quot;</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #ABB2BF">class1?.</span><span style="color: #E06C75">class2</span><span style="color: #ABB2BF"> = class2 </span><span style="color: #7F848E; font-style: italic">// class1 holds class2</span></span><span class="line"><span style="color: #ABB2BF">class2?.</span><span style="color: #E06C75">class1</span><span style="color: #ABB2BF"> = class1 </span><span style="color: #7F848E; font-style: italic">// class2 holds class1 (cycle!)</span></span><span class="line"></span><span class="line"><span style="color: #ABB2BF">class1 = </span><span style="color: #D19A66">nil</span></span><span class="line"><span style="color: #ABB2BF">class2 = </span><span style="color: #D19A66">nil</span><span style="color: #ABB2BF"> </span><span style="color: #7F848E; font-style: italic">// Instances will not be released</span></span></code></pre></div></div></figure><p><strong>Solutions</strong>:</p><ul><li><p><strong>Weak References (<code>weak</code>)</strong>: Use when the referenced instance might become <code>nil</code>. A weak reference does not increase the reference count and must be an optional type.</p><figure class="shiki swift"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"><span style="color: #C678DD">class</span><span style="color: #ABB2BF"> </span><span style="color: #E5C07B">MyClass2_Weak</span><span style="color: #ABB2BF"> {</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">weak</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">var</span><span style="color: #ABB2BF"> class1: MyClass1_Weak? </span><span style="color: #7F848E; font-style: italic">// Weak reference</span></span><span class="line"><span style="color: #ABB2BF">}</span></span></code></pre></div></div></figure></li><li><p><strong>Unowned References (<code>unowned</code>)</strong>: Use when you are certain the referenced instance will never be <code>nil</code> during the current instance’s lifetime. An unowned reference is not optional, and accessing a deallocated unowned reference will trigger a runtime error.</p><figure class="shiki swift"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"><span style="color: #C678DD">class</span><span style="color: #ABB2BF"> </span><span style="color: #E5C07B">MyClass1_Unowned</span><span style="color: #ABB2BF"> {</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">unowned</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> class2: MyClass2_Unowned </span><span style="color: #7F848E; font-style: italic">// Unowned reference</span></span><span class="line"><span style="color: #ABB2BF">}</span></span></code></pre></div></div></figure></li></ul><hr><h2 id="Part-2-Protocol-Oriented-Functional-Programming"><a href="#Part-2-Protocol-Oriented-Functional-Programming" class="headerlink" title="Part 2: Protocol-Oriented &amp; Functional Programming"></a>Part 2: Protocol-Oriented &amp; Functional Programming</h2><p>Swift is a multi-paradigm language with strong support for Protocol-Oriented Programming (POP) and Functional Programming.</p><h3 id="1-Object-Oriented-Programming-OOP"><a href="#1-Object-Oriented-Programming-OOP" class="headerlink" title="1. Object-Oriented Programming (OOP)"></a>1. Object-Oriented Programming (OOP)</h3><p>The three pillars of OOP are Encapsulation, Inheritance, and Polymorphism.</p><ul><li><strong>Inheritance</strong>: Supported by reference types (<code>class</code>). While powerful, complex class hierarchies can increase code complexity and coupling, making modification and maintenance difficult.</li><li><strong>Dynamic Dispatch</strong>: When a method on a class is called, the runtime uses a virtual table (VTable) to look up and call the correct implementation. This provides flexibility but is slightly slower than a direct call.</li></ul><h3 id="2-Protocol-Oriented-Programming-POP"><a href="#2-Protocol-Oriented-Programming-POP" class="headerlink" title="2. Protocol-Oriented Programming (POP)"></a>2. Protocol-Oriented Programming (POP)</h3><p>POP is a core design philosophy in Swift. It emphasizes defining blueprints using protocols rather than relying on class inheritance.</p><ul><li><strong>Protocol Definition</strong>: A protocol defines a blueprint of methods, properties, and other requirements.<figure class="shiki swift"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"><span style="color: #C678DD">protocol</span><span style="color: #ABB2BF"> </span><span style="color: #E5C07B">Nameable</span><span style="color: #ABB2BF"> {</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">var</span><span style="color: #ABB2BF"> firstName: </span><span style="color: #E5C07B">String</span><span style="color: #ABB2BF"> { </span><span style="color: #C678DD">get</span><span style="color: #ABB2BF"> }</span></span><span class="line"><span style="color: #ABB2BF">}</span></span></code></pre></div></div></figure></li><li><strong>Protocol Composition</strong>: A type can conform to multiple protocols, combining different functionalities.<figure class="shiki swift"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"><span style="color: #C678DD">protocol</span><span style="color: #ABB2BF"> </span><span style="color: #E5C07B">Person</span><span style="color: #ABB2BF">: </span><span style="color: #E5C07B">Nameable</span><span style="color: #ABB2BF">, </span><span style="color: #E5C07B">Contactable </span><span style="color: #ABB2BF">{ </span><span style="color: #7F848E; font-style: italic">/* ... */</span><span style="color: #ABB2BF"> }</span></span><span class="line"><span style="color: #C678DD">struct</span><span style="color: #ABB2BF"> </span><span style="color: #E5C07B">Employee</span><span style="color: #ABB2BF">: </span><span style="color: #E5C07B">Person</span><span style="color: #ABB2BF">, </span><span style="color: #E5C07B">Occupation </span><span style="color: #ABB2BF">{ </span><span style="color: #7F848E; font-style: italic">/* ... */</span><span style="color: #ABB2BF"> }</span></span></code></pre></div></div></figure></li><li><strong>Protocol Inheritance</strong>: Protocols can also inherit from other protocols, aggregating multiple requirements.</li></ul><p>Compared to class inheritance, POP offers greater flexibility and modularity, helping to avoid bloated base classes.</p><h3 id="3-Protocols-and-Protocol-Extensions"><a href="#3-Protocols-and-Protocol-Extensions" class="headerlink" title="3. Protocols and Protocol Extensions"></a>3. Protocols and Protocol Extensions</h3><ul><li><p><strong>Type Checking and Casting</strong>: You can use <code>is</code> and <code>as?</code> to check if an instance conforms to a protocol.</p><figure class="shiki swift"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"><span style="color: #C678DD">for</span><span style="color: #ABB2BF"> person </span><span style="color: #C678DD">in</span><span style="color: #ABB2BF"> people </span><span style="color: #C678DD">where</span><span style="color: #ABB2BF"> person is SwiftProgrammer {</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #56B6C2">print</span><span style="color: #ABB2BF">(</span><span style="color: #98C379">&quot;</span><span style="color: #C678DD">\(</span><span style="color: #ABB2BF">person.</span><span style="color: #E06C75">firstName</span><span style="color: #C678DD">)</span><span style="color: #98C379"> is a Swift Programmer&quot;</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #ABB2BF">}</span></span></code></pre></div></div></figure></li><li><p><strong>Protocol Extensions</strong>: You can extend protocols to provide default implementations for methods. Types conforming to the protocol automatically gain this functionality.</p><figure class="shiki swift"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"><span style="color: #C678DD">protocol</span><span style="color: #ABB2BF"> </span><span style="color: #E5C07B">Dog</span><span style="color: #ABB2BF"> {</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">var</span><span style="color: #ABB2BF"> name: </span><span style="color: #E5C07B">String</span><span style="color: #ABB2BF"> { </span><span style="color: #C678DD">get</span><span style="color: #ABB2BF"> }</span></span><span class="line"><span style="color: #ABB2BF">}</span></span><span class="line"><span style="color: #C678DD">extension</span><span style="color: #ABB2BF"> </span><span style="color: #E5C07B">Dog</span><span style="color: #ABB2BF"> {</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">func</span><span style="color: #ABB2BF"> </span><span style="color: #61AFEF">speak</span><span style="color: #ABB2BF">() -&gt; </span><span style="color: #E5C07B">String</span><span style="color: #ABB2BF"> {</span></span><span class="line"><span style="color: #ABB2BF">        </span><span style="color: #C678DD">return</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">&quot;Woof Woof&quot;</span></span><span class="line"><span style="color: #ABB2BF">    }</span></span><span class="line"><span style="color: #ABB2BF">}</span></span></code></pre></div></div></figure></li><li><p><strong><code>Any</code> vs <code>any</code></strong>:</p><ul><li><code>Any</code>: Can represent a value of any type, including function and optional types.</li><li><code>any</code>: Used to modify a protocol, representing an existential type. It allows you to store values of different types that conform to the same protocol in a container and supports dynamic dispatch.</li></ul></li><li><p><strong>Implicitly Opened Existentials</strong>: Swift 6 allows the compiler to implicitly open existential types, simplifying operations on protocol arrays.</p><figure class="shiki swift"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"><span style="color: #C678DD">protocol</span><span style="color: #ABB2BF"> </span><span style="color: #E5C07B">Drawable</span><span style="color: #ABB2BF"> { </span><span style="color: #C678DD">func</span><span style="color: #ABB2BF"> </span><span style="color: #61AFEF">draw</span><span style="color: #ABB2BF">() }</span></span><span class="line"><span style="color: #7F848E; font-style: italic">// No manual casting needed, protocol methods can be called directly</span></span><span class="line"><span style="color: #C678DD">func</span><span style="color: #ABB2BF"> </span><span style="color: #61AFEF">drawAll</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">_</span><span style="color: #ABB2BF"> </span><span style="color: #ABB2BF; font-style: italic">items</span><span style="color: #ABB2BF">: [</span><span style="color: #C678DD">any</span><span style="color: #ABB2BF"> Drawable]) {</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">for</span><span style="color: #ABB2BF"> item </span><span style="color: #C678DD">in</span><span style="color: #ABB2BF"> items {</span></span><span class="line"><span style="color: #ABB2BF">        item.</span><span style="color: #61AFEF">draw</span><span style="color: #ABB2BF">()</span></span><span class="line"><span style="color: #ABB2BF">    }</span></span><span class="line"><span style="color: #ABB2BF">}</span></span></code></pre></div></div></figure></li></ul><h3 id="4-Generics"><a href="#4-Generics" class="headerlink" title="4. Generics"></a>4. Generics</h3><p>Generic code enables you to write flexible, reusable functions and types that can work with any type.</p><ul><li><strong>Generic Functions</strong>:<figure class="shiki swift"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"><span style="color: #C678DD">func</span><span style="color: #ABB2BF"> </span><span style="color: #61AFEF">swapGeneric</span><span style="color: #ABB2BF">&lt;</span><span style="color: #E5C07B">T</span><span style="color: #ABB2BF">&gt;(</span><span style="color: #61AFEF; font-style: italic">a</span><span style="color: #ABB2BF">: </span><span style="color: #C678DD">inout</span><span style="color: #ABB2BF"> T, </span><span style="color: #61AFEF; font-style: italic">b</span><span style="color: #ABB2BF">: </span><span style="color: #C678DD">inout</span><span style="color: #ABB2BF"> T) {</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> tmp = a; a = b; b = tmp</span></span><span class="line"><span style="color: #ABB2BF">}</span></span></code></pre></div></div></figure></li><li><strong>Associated Types (<code>associatedtype</code>)</strong>: Used in a protocol as a placeholder for a type that is specified only when the protocol is adopted.<figure class="shiki swift"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"><span style="color: #C678DD">protocol</span><span style="color: #ABB2BF"> </span><span style="color: #E5C07B">Queue</span><span style="color: #ABB2BF"> {</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">associatedtype</span><span style="color: #ABB2BF"> </span><span style="color: #E5C07B">QueueType</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">mutating</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">func</span><span style="color: #ABB2BF"> </span><span style="color: #61AFEF">add</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF; font-style: italic">item</span><span style="color: #ABB2BF">: QueueType)</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">mutating</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">func</span><span style="color: #ABB2BF"> </span><span style="color: #61AFEF">getItem</span><span style="color: #ABB2BF">() -&gt; QueueType?</span></span><span class="line"><span style="color: #ABB2BF">}</span></span></code></pre></div></div></figure></li><li><strong>Conditional Extensions and Conformance</strong>: You can add extensions to a generic type that are only available if the generic parameter meets certain conditions.<figure class="shiki swift"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"><span style="color: #7F848E; font-style: italic">// Add the sum method only if T is a numeric type</span></span><span class="line"><span style="color: #C678DD">extension</span><span style="color: #ABB2BF"> </span><span style="color: #E5C07B">List</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">where</span><span style="color: #ABB2BF"> T: </span><span style="color: #E5C07B">Numeric</span><span style="color: #ABB2BF"> {</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">func</span><span style="color: #ABB2BF"> </span><span style="color: #61AFEF">sum</span><span style="color: #ABB2BF">() -&gt; T { items.</span><span style="color: #56B6C2">reduce</span><span style="color: #ABB2BF">(</span><span style="color: #D19A66">0</span><span style="color: #ABB2BF">, +) }</span></span><span class="line"><span style="color: #ABB2BF">}</span></span><span class="line"></span><span class="line"><span style="color: #7F848E; font-style: italic">// List conforms to Equatable only if T also conforms to Equatable</span></span><span class="line"><span style="color: #C678DD">extension</span><span style="color: #ABB2BF"> </span><span style="color: #E5C07B">List</span><span style="color: #ABB2BF">: </span><span style="color: #E5C07B">Equatable </span><span style="color: #C678DD">where</span><span style="color: #ABB2BF"> T: </span><span style="color: #E5C07B">Equatable</span><span style="color: #ABB2BF"> {</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">static</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">func</span><span style="color: #ABB2BF"> </span><span style="color: #61AFEF">==</span><span style="color: #ABB2BF"> (</span><span style="color: #61AFEF; font-style: italic">l1</span><span style="color: #ABB2BF">: List, </span><span style="color: #61AFEF; font-style: italic">l2</span><span style="color: #ABB2BF">: List) -&gt; </span><span style="color: #E5C07B">Bool</span><span style="color: #ABB2BF"> {</span></span><span class="line"><span style="color: #ABB2BF">        </span><span style="color: #7F848E; font-style: italic">// ... comparison logic</span></span><span class="line"><span style="color: #ABB2BF">    }</span></span><span class="line"><span style="color: #ABB2BF">}</span></span></code></pre></div></div></figure></li></ul><h3 id="5-Functional-Programming"><a href="#5-Functional-Programming" class="headerlink" title="5. Functional Programming"></a>5. Functional Programming</h3><p>Core principles of functional programming include:</p><ul><li><strong>Immutability</strong>: Prefer constants (<code>let</code>) to avoid direct state modification.<figure class="shiki swift"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> numbers = [</span><span style="color: #D19A66">1</span><span style="color: #ABB2BF">, </span><span style="color: #D19A66">2</span><span style="color: #ABB2BF">, </span><span style="color: #D19A66">3</span><span style="color: #ABB2BF">, </span><span style="color: #D19A66">4</span><span style="color: #ABB2BF">, </span><span style="color: #D19A66">5</span><span style="color: #ABB2BF">]</span></span><span class="line"><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> doubled = numbers.</span><span style="color: #56B6C2">map</span><span style="color: #ABB2BF"> { </span><span style="color: #E5C07B">$0</span><span style="color: #ABB2BF"> * </span><span style="color: #D19A66">2</span><span style="color: #ABB2BF"> } </span><span style="color: #7F848E; font-style: italic">// The `numbers` array itself is not changed</span></span></code></pre></div></div></figure></li><li><strong>Pure Functions</strong>: Always produce the same output for the same input and have no side effects.<figure class="shiki swift"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"><span style="color: #C678DD">func</span><span style="color: #ABB2BF"> </span><span style="color: #61AFEF">add</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">_</span><span style="color: #ABB2BF"> </span><span style="color: #ABB2BF; font-style: italic">first</span><span style="color: #ABB2BF">: </span><span style="color: #E5C07B">Int</span><span style="color: #ABB2BF">, </span><span style="color: #61AFEF">_</span><span style="color: #ABB2BF"> </span><span style="color: #ABB2BF; font-style: italic">second</span><span style="color: #ABB2BF">: </span><span style="color: #E5C07B">Int</span><span style="color: #ABB2BF">) -&gt; </span><span style="color: #E5C07B">Int</span><span style="color: #ABB2BF"> {</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">return</span><span style="color: #ABB2BF"> first + second</span></span><span class="line"><span style="color: #ABB2BF">}</span></span></code></pre></div></div></figure></li><li><strong>Higher-Order Functions</strong>: Functions that take other functions as arguments or return them, such as <code>map</code>, <code>filter</code>, and <code>reduce</code>.<figure class="shiki swift"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"><span style="color: #C678DD">func</span><span style="color: #ABB2BF"> </span><span style="color: #61AFEF">performMathOperation</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">_</span><span style="color: #ABB2BF"> </span><span style="color: #ABB2BF; font-style: italic">first</span><span style="color: #ABB2BF">: </span><span style="color: #E5C07B">UInt</span><span style="color: #ABB2BF">, </span><span style="color: #61AFEF">_</span><span style="color: #ABB2BF"> </span><span style="color: #ABB2BF; font-style: italic">second</span><span style="color: #ABB2BF">: </span><span style="color: #E5C07B">UInt</span><span style="color: #ABB2BF">, </span><span style="color: #61AFEF; font-style: italic">function</span><span style="color: #ABB2BF">: (</span><span style="color: #E5C07B">UInt</span><span style="color: #ABB2BF">, </span><span style="color: #E5C07B">UInt</span><span style="color: #ABB2BF">) -&gt; </span><span style="color: #E5C07B">UInt</span><span style="color: #ABB2BF">) -&gt; </span><span style="color: #E5C07B">UInt</span><span style="color: #ABB2BF"> {</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">return</span><span style="color: #ABB2BF"> </span><span style="color: #61AFEF">function</span><span style="color: #ABB2BF">(first, second)</span></span><span class="line"><span style="color: #ABB2BF">}</span></span></code></pre></div></div></figure></li></ul><p><strong>Advanced Techniques</strong>:</p><ul><li><strong>Function Composition</strong>: Combining multiple functions into a new one.<figure class="shiki swift"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"><span style="color: #C678DD">infix</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">operator</span><span style="color: #ABB2BF"> </span><span style="color: #61AFEF">&gt;&gt;&gt;</span></span><span class="line"><span style="color: #C678DD">func</span><span style="color: #ABB2BF"> </span><span style="color: #61AFEF">&gt;&gt;&gt;</span><span style="color: #ABB2BF"> &lt;</span><span style="color: #E5C07B">A</span><span style="color: #ABB2BF">, </span><span style="color: #E5C07B">B</span><span style="color: #ABB2BF">, </span><span style="color: #E5C07B">C</span><span style="color: #ABB2BF">&gt;(</span><span style="color: #61AFEF; font-style: italic">lhs</span><span style="color: #ABB2BF">: </span><span style="color: #C678DD">@escaping</span><span style="color: #ABB2BF"> (A) -&gt; B, </span><span style="color: #61AFEF; font-style: italic">rhs</span><span style="color: #ABB2BF">: </span><span style="color: #C678DD">@escaping</span><span style="color: #ABB2BF"> (B) -&gt; C) -&gt; (A) -&gt; C {</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">return</span><span style="color: #ABB2BF"> { </span><span style="color: #61AFEF">rhs</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">lhs</span><span style="color: #ABB2BF">(</span><span style="color: #E5C07B">$0</span><span style="color: #ABB2BF">)) }</span></span><span class="line"><span style="color: #ABB2BF">}</span></span><span class="line"><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> addOneToString = addOne &gt;&gt;&gt; toString</span></span></code></pre></div></div></figure></li><li><strong>Currying</strong>: Transforming a function that takes multiple arguments into a sequence of functions that each take a single argument.<figure class="shiki swift"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"><span style="color: #C678DD">func</span><span style="color: #ABB2BF"> </span><span style="color: #61AFEF">curriedAdd</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">_</span><span style="color: #ABB2BF"> </span><span style="color: #ABB2BF; font-style: italic">a</span><span style="color: #ABB2BF">: </span><span style="color: #E5C07B">Int</span><span style="color: #ABB2BF">) -&gt; (</span><span style="color: #E5C07B">Int</span><span style="color: #ABB2BF">) -&gt; </span><span style="color: #E5C07B">Int</span><span style="color: #ABB2BF"> {</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">return</span><span style="color: #ABB2BF"> { a + </span><span style="color: #E5C07B">$0</span><span style="color: #ABB2BF"> }</span></span><span class="line"><span style="color: #ABB2BF">}</span></span><span class="line"><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> addTwo = </span><span style="color: #61AFEF">curriedAdd</span><span style="color: #ABB2BF">(</span><span style="color: #D19A66">2</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> result = </span><span style="color: #61AFEF">addTwo</span><span style="color: #ABB2BF">(</span><span style="color: #D19A66">3</span><span style="color: #ABB2BF">) </span><span style="color: #7F848E; font-style: italic">// result is 5</span></span></code></pre></div></div></figure></li><li><strong>Recursion</strong>: A function calling itself to solve a problem.<figure class="shiki swift"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"><span style="color: #C678DD">func</span><span style="color: #ABB2BF"> </span><span style="color: #61AFEF">factorial</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">_</span><span style="color: #ABB2BF"> </span><span style="color: #ABB2BF; font-style: italic">n</span><span style="color: #ABB2BF">: </span><span style="color: #E5C07B">Int</span><span style="color: #ABB2BF">) -&gt; </span><span style="color: #E5C07B">Int</span><span style="color: #ABB2BF"> {</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">if</span><span style="color: #ABB2BF"> n &lt;= </span><span style="color: #D19A66">1</span><span style="color: #ABB2BF"> { </span><span style="color: #C678DD">return</span><span style="color: #ABB2BF"> </span><span style="color: #D19A66">1</span><span style="color: #ABB2BF"> }</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">return</span><span style="color: #ABB2BF"> n * </span><span style="color: #61AFEF">factorial</span><span style="color: #ABB2BF">(n - </span><span style="color: #D19A66">1</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #ABB2BF">}</span></span></code></pre></div></div></figure></li></ul><hr><h2 id="Part-3-Modern-Concurrency"><a href="#Part-3-Modern-Concurrency" class="headerlink" title="Part 3: Modern Concurrency"></a>Part 3: Modern Concurrency</h2><p>Swift provides a concurrency model ranging from the low-level GCD to the modern <code>async/await</code> structured approach.</p><h3 id="1-Grand-Central-Dispatch-GCD"><a href="#1-Grand-Central-Dispatch-GCD" class="headerlink" title="1. Grand Central Dispatch (GCD)"></a>1. Grand Central Dispatch (GCD)</h3><p>GCD is a low-level C API that manages tasks via queues.</p><ul><li><strong>Concurrency</strong>: Multiple tasks starting, running, and completing in the same time period.</li><li><strong>Parallelism</strong>: Multiple tasks running at the exact same moment, which requires a multi-core processor.</li></ul><p><strong>Queue Types</strong>:</p><ul><li><strong>Serial Queues</strong>: Tasks are executed one at a time in FIFO order. Often used to synchronize access to a shared resource.</li><li><strong>Concurrent Queues</strong>: Tasks start in order but can run concurrently. The system determines the number of concurrent tasks.</li><li><strong>Main Dispatch Queue</strong>: A globally available serial queue that executes tasks on the application’s main thread, typically used for UI updates.</li></ul><p><strong>Using Queues</strong>:</p><figure class="shiki swift"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"><span style="color: #7F848E; font-style: italic">// Create a concurrent queue</span></span><span class="line"><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> cqueue = </span><span style="color: #61AFEF">DispatchQueue</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">label</span><span style="color: #ABB2BF">: </span><span style="color: #98C379">&quot;cqueue.example&quot;</span><span style="color: #ABB2BF">, </span><span style="color: #61AFEF">attributes</span><span style="color: #ABB2BF">: .</span><span style="color: #E06C75">concurrent</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #7F848E; font-style: italic">// Create a serial queue</span></span><span class="line"><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> squeue = </span><span style="color: #61AFEF">DispatchQueue</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">label</span><span style="color: #ABB2BF">: </span><span style="color: #98C379">&quot;squeue.example&quot;</span><span style="color: #ABB2BF">)</span></span><span class="line"></span><span class="line"><span style="color: #7F848E; font-style: italic">// Execute a task asynchronously without blocking the current thread</span></span><span class="line"><span style="color: #ABB2BF">cqueue.</span><span style="color: #61AFEF">async</span><span style="color: #ABB2BF"> {</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #61AFEF">performCalculation</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">tag</span><span style="color: #ABB2BF">: </span><span style="color: #98C379">&quot;async1&quot;</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #ABB2BF">}</span></span><span class="line"></span><span class="line"><span style="color: #7F848E; font-style: italic">// Switch from a background thread to the main thread to update the UI</span></span><span class="line"><span style="color: #ABB2BF">squeue.</span><span style="color: #61AFEF">async</span><span style="color: #ABB2BF"> {</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> resizedImage = image.</span><span style="color: #61AFEF">resize</span><span style="color: #ABB2BF">()</span></span><span class="line"><span style="color: #ABB2BF">    DispatchQueue.</span><span style="color: #E06C75">main</span><span style="color: #ABB2BF">.</span><span style="color: #61AFEF">async</span><span style="color: #ABB2BF"> {</span></span><span class="line"><span style="color: #ABB2BF">        picView.image = resizedImage</span></span><span class="line"><span style="color: #ABB2BF">    }</span></span><span class="line"><span style="color: #ABB2BF">}</span></span></code></pre></div></div></figure><p><strong>Advanced Tools</strong>:</p><ul><li><strong><code>DispatchGroup</code></strong>: Coordinate the completion of multiple asynchronous tasks.<figure class="shiki swift"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> group = </span><span style="color: #61AFEF">DispatchGroup</span><span style="color: #ABB2BF">()</span></span><span class="line"><span style="color: #ABB2BF">group.</span><span style="color: #61AFEF">enter</span><span style="color: #ABB2BF">()</span></span><span class="line"><span style="color: #ABB2BF">queue.</span><span style="color: #61AFEF">async</span><span style="color: #ABB2BF"> { </span><span style="color: #7F848E; font-style: italic">/* task 1 */</span><span style="color: #ABB2BF">; group.</span><span style="color: #61AFEF">leave</span><span style="color: #ABB2BF">() }</span></span><span class="line"><span style="color: #ABB2BF">group.</span><span style="color: #61AFEF">enter</span><span style="color: #ABB2BF">()</span></span><span class="line"><span style="color: #ABB2BF">queue.</span><span style="color: #61AFEF">async</span><span style="color: #ABB2BF"> { </span><span style="color: #7F848E; font-style: italic">/* task 2 */</span><span style="color: #ABB2BF">; group.</span><span style="color: #61AFEF">leave</span><span style="color: #ABB2BF">() }</span></span><span class="line"></span><span class="line"><span style="color: #ABB2BF">group.</span><span style="color: #61AFEF">notify</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">queue</span><span style="color: #ABB2BF">: .</span><span style="color: #E06C75">main</span><span style="color: #ABB2BF">) {</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #56B6C2">print</span><span style="color: #ABB2BF">(</span><span style="color: #98C379">&quot;All tasks are complete&quot;</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #ABB2BF">}</span></span></code></pre></div></div></figure></li><li><strong><code>Barrier</code></strong>: Create a synchronization point in a concurrent queue. All tasks submitted before the barrier complete before the barrier task executes. Tasks submitted after the barrier wait for it to finish.<figure class="shiki swift"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"><span style="color: #ABB2BF">queue.</span><span style="color: #61AFEF">async</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">flags</span><span style="color: #ABB2BF">: .</span><span style="color: #E06C75">barrier</span><span style="color: #ABB2BF">) {</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #7F848E; font-style: italic">// This task waits for previous tasks and blocks subsequent ones</span></span><span class="line"><span style="color: #ABB2BF">}</span></span></code></pre></div></div></figure></li><li><strong><code>DispatchSemaphore</code></strong>: Control the number of concurrent accesses to a shared resource.<figure class="shiki swift"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> semaphore = </span><span style="color: #61AFEF">DispatchSemaphore</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">value</span><span style="color: #ABB2BF">: </span><span style="color: #D19A66">1</span><span style="color: #ABB2BF">) </span><span style="color: #7F848E; font-style: italic">// Allow only one thread to access</span></span><span class="line"><span style="color: #C678DD">func</span><span style="color: #ABB2BF"> </span><span style="color: #61AFEF">accessSharedResource</span><span style="color: #ABB2BF">() {</span></span><span class="line"><span style="color: #ABB2BF">    semaphore.</span><span style="color: #61AFEF">wait</span><span style="color: #ABB2BF">() </span><span style="color: #7F848E; font-style: italic">// Request access, wait if count is 0</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #7F848E; font-style: italic">// ... access shared resource ...</span></span><span class="line"><span style="color: #ABB2BF">    semaphore.</span><span style="color: #61AFEF">signal</span><span style="color: #ABB2BF">() </span><span style="color: #7F848E; font-style: italic">// Finished, release the resource</span></span><span class="line"><span style="color: #ABB2BF">}</span></span></code></pre></div></div></figure></li></ul><h3 id="2-Structured-Concurrency-async-await"><a href="#2-Structured-Concurrency-async-await" class="headerlink" title="2. Structured Concurrency (async/await)"></a>2. Structured Concurrency (<code>async/await</code>)</h3><p>Swift 6 emphasizes using structured concurrency to write safer, more readable asynchronous code, preventing issues like data races.</p><h4 id="async-and-await"><a href="#async-and-await" class="headerlink" title="async and await"></a><code>async</code> and <code>await</code></h4><ul><li><code>async</code>: Marks a function as asynchronous, meaning it can be suspended during its execution.</li><li><code>await</code>: Used to call an <code>async</code> function, indicating that the current task might pause here to wait for the result of the asynchronous function.</li></ul><!-- end list --><figure class="shiki swift"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"><span style="color: #C678DD">func</span><span style="color: #ABB2BF"> </span><span style="color: #61AFEF">retrieveUserData</span><span style="color: #ABB2BF">() </span><span style="color: #C678DD">async</span><span style="color: #ABB2BF"> -&gt; </span><span style="color: #E5C07B">String</span><span style="color: #ABB2BF"> {</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #56B6C2">print</span><span style="color: #ABB2BF">(</span><span style="color: #98C379">&quot;Retrieving user data&quot;</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">try</span><span style="color: #ABB2BF">? </span><span style="color: #C678DD">await</span><span style="color: #ABB2BF"> Task.</span><span style="color: #61AFEF">sleep</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">nanoseconds</span><span style="color: #ABB2BF">: </span><span style="color: #D19A66">2_000_000_000</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">return</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">&quot;User Data Retrieved&quot;</span></span><span class="line"><span style="color: #ABB2BF">}</span></span><span class="line"></span><span class="line"><span style="color: #7F848E; font-style: italic">// Serial execution</span></span><span class="line"><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> data = </span><span style="color: #C678DD">await</span><span style="color: #ABB2BF"> </span><span style="color: #61AFEF">retrieveUserData</span><span style="color: #ABB2BF">()</span></span><span class="line"></span><span class="line"><span style="color: #7F848E; font-style: italic">// Parallel execution</span></span><span class="line"><span style="color: #C678DD">async</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> userData = </span><span style="color: #61AFEF">retrieveUserData</span><span style="color: #ABB2BF">()</span></span><span class="line"><span style="color: #C678DD">async</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> imageData = </span><span style="color: #61AFEF">retrieveImageData</span><span style="color: #ABB2BF">()</span></span><span class="line"><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> results = </span><span style="color: #C678DD">await</span><span style="color: #ABB2BF"> (userData, imageData)</span></span><span class="line"><span style="color: #56B6C2">print</span><span style="color: #ABB2BF">(</span><span style="color: #98C379">&quot;User Data: </span><span style="color: #C678DD">\(</span><span style="color: #ABB2BF">results.0</span><span style="color: #C678DD">)</span><span style="color: #98C379">, Image Data: </span><span style="color: #C678DD">\(</span><span style="color: #ABB2BF">results.1</span><span style="color: #C678DD">)</span><span style="color: #98C379">&quot;</span><span style="color: #ABB2BF">)</span></span></code></pre></div></div></figure><h4 id="Tasks"><a href="#Tasks" class="headerlink" title="Tasks"></a>Tasks</h4><p>A <code>Task</code> represents a unit of work that can be run asynchronously.</p><ul><li><strong>Creating a Task</strong>:<figure class="shiki swift"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"><span style="color: #61AFEF">Task</span><span style="color: #ABB2BF"> {</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> data = </span><span style="color: #C678DD">await</span><span style="color: #ABB2BF"> </span><span style="color: #61AFEF">retrieveUserData</span><span style="color: #ABB2BF">()</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #56B6C2">print</span><span style="color: #ABB2BF">(</span><span style="color: #98C379">&quot;Data: </span><span style="color: #C678DD">\(</span><span style="color: #ABB2BF">data</span><span style="color: #C678DD">)</span><span style="color: #98C379">&quot;</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #ABB2BF">}</span></span></code></pre></div></div></figure></li><li><strong>Detached Task (<code>Task.detached</code>)</strong>: Creates a top-level task that does not inherit the context (like actor isolation) of its creation point.</li><li><strong>Task Cancellation</strong>: Tasks can be cancelled externally. In a long-running loop, you should periodically check <code>Task.isCancelled</code> and exit gracefully.<figure class="shiki swift"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"><span style="color: #C678DD">func</span><span style="color: #ABB2BF"> </span><span style="color: #61AFEF">testCancelTask</span><span style="color: #ABB2BF">() </span><span style="color: #C678DD">async</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">throws</span><span style="color: #ABB2BF"> {</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">for</span><span style="color: #ABB2BF"> i </span><span style="color: #C678DD">in</span><span style="color: #ABB2BF"> </span><span style="color: #D19A66">0</span><span style="color: #ABB2BF">..&lt;</span><span style="color: #D19A66">10</span><span style="color: #ABB2BF"> {</span></span><span class="line"><span style="color: #ABB2BF">        </span><span style="color: #C678DD">if</span><span style="color: #ABB2BF"> Task.isCancelled {</span></span><span class="line"><span style="color: #ABB2BF">            </span><span style="color: #56B6C2">print</span><span style="color: #ABB2BF">(</span><span style="color: #98C379">&quot;Task was cancelled, cleaning up&quot;</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #ABB2BF">            </span><span style="color: #C678DD">throw</span><span style="color: #ABB2BF"> </span><span style="color: #61AFEF">CancellationError</span><span style="color: #ABB2BF">()</span></span><span class="line"><span style="color: #ABB2BF">        }</span></span><span class="line"><span style="color: #ABB2BF">        </span><span style="color: #56B6C2">print</span><span style="color: #ABB2BF">(</span><span style="color: #98C379">&quot;Loop </span><span style="color: #C678DD">\(</span><span style="color: #ABB2BF">i</span><span style="color: #C678DD">)</span><span style="color: #98C379">&quot;</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #ABB2BF">        </span><span style="color: #C678DD">await</span><span style="color: #ABB2BF"> </span><span style="color: #61AFEF">retrieveUserData</span><span style="color: #ABB2BF">()</span></span><span class="line"><span style="color: #ABB2BF">    }</span></span><span class="line"><span style="color: #ABB2BF">}</span></span></code></pre></div></div></figure></li></ul><h4 id="Task-Groups"><a href="#Task-Groups" class="headerlink" title="Task Groups"></a>Task Groups</h4><p>Used for creating a dynamic group of concurrent tasks and waiting for all of them to complete.</p><figure class="shiki swift"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"><span style="color: #C678DD">func</span><span style="color: #ABB2BF"> </span><span style="color: #61AFEF">taskGroup</span><span style="color: #ABB2BF">() </span><span style="color: #C678DD">async</span><span style="color: #ABB2BF"> -&gt; [</span><span style="color: #E5C07B">String</span><span style="color: #ABB2BF">] {</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">return</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">await</span><span style="color: #ABB2BF"> </span><span style="color: #61AFEF">withTaskGroup</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">of</span><span style="color: #ABB2BF">: </span><span style="color: #E5C07B">String</span><span style="color: #ABB2BF">.</span><span style="color: #C678DD">self</span><span style="color: #ABB2BF">) { group </span><span style="color: #C678DD">in</span></span><span class="line"><span style="color: #ABB2BF">        </span><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> users = [</span><span style="color: #98C379">&quot;Jon&quot;</span><span style="color: #ABB2BF">, </span><span style="color: #98C379">&quot;Heidi&quot;</span><span style="color: #ABB2BF">, </span><span style="color: #98C379">&quot;Kailey&quot;</span><span style="color: #ABB2BF">, </span><span style="color: #98C379">&quot;Kai&quot;</span><span style="color: #ABB2BF">]</span></span><span class="line"><span style="color: #ABB2BF">        </span><span style="color: #C678DD">for</span><span style="color: #ABB2BF"> user </span><span style="color: #C678DD">in</span><span style="color: #ABB2BF"> users {</span></span><span class="line"><span style="color: #ABB2BF">            group.</span><span style="color: #61AFEF">addTask</span><span style="color: #ABB2BF"> {</span></span><span class="line"><span style="color: #ABB2BF">                </span><span style="color: #C678DD">return</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">await</span><span style="color: #ABB2BF"> </span><span style="color: #61AFEF">retrieveUserData</span><span style="color: #ABB2BF">(user)</span></span><span class="line"><span style="color: #ABB2BF">            }</span></span><span class="line"><span style="color: #ABB2BF">        }</span></span><span class="line"><span style="color: #ABB2BF">        </span></span><span class="line"><span style="color: #ABB2BF">        </span><span style="color: #C678DD">var</span><span style="color: #ABB2BF"> data = [</span><span style="color: #E5C07B">String</span><span style="color: #ABB2BF">]()</span></span><span class="line"><span style="color: #ABB2BF">        </span><span style="color: #C678DD">for</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">await</span><span style="color: #ABB2BF"> result </span><span style="color: #C678DD">in</span><span style="color: #ABB2BF"> group {</span></span><span class="line"><span style="color: #ABB2BF">            data.</span><span style="color: #56B6C2">append</span><span style="color: #ABB2BF">(result)</span></span><span class="line"><span style="color: #ABB2BF">        }</span></span><span class="line"><span style="color: #ABB2BF">        </span><span style="color: #C678DD">return</span><span style="color: #ABB2BF"> data</span></span><span class="line"><span style="color: #ABB2BF">    }</span></span><span class="line"><span style="color: #ABB2BF">}</span></span></code></pre></div></div></figure><h4 id="Actors"><a href="#Actors" class="headerlink" title="Actors"></a>Actors</h4><p>Actors are a special kind of reference type that protect their mutable state from concurrent access, preventing data races. Access to an actor’s internal state is asynchronous and serialized.</p><figure class="shiki swift"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"><span style="color: #C678DD">actor</span><span style="color: #ABB2BF"> </span><span style="color: #E5C07B">BankAccount</span><span style="color: #ABB2BF"> {</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">private</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">var</span><span style="color: #ABB2BF"> balance: </span><span style="color: #E5C07B">Double</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">init</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">_</span><span style="color: #ABB2BF"> </span><span style="color: #ABB2BF; font-style: italic">balance</span><span style="color: #ABB2BF">: </span><span style="color: #E5C07B">Double</span><span style="color: #ABB2BF">) { </span><span style="color: #E5C07B">self</span><span style="color: #ABB2BF">.</span><span style="color: #E06C75">balance</span><span style="color: #ABB2BF"> = balance }</span></span><span class="line"><span style="color: #ABB2BF">    </span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">func</span><span style="color: #ABB2BF"> </span><span style="color: #61AFEF">deposit</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF; font-style: italic">amount</span><span style="color: #ABB2BF">: </span><span style="color: #E5C07B">Double</span><span style="color: #ABB2BF">) {</span></span><span class="line"><span style="color: #ABB2BF">        balance += amount</span></span><span class="line"><span style="color: #ABB2BF">    }</span></span><span class="line"><span style="color: #ABB2BF">    </span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">func</span><span style="color: #ABB2BF"> </span><span style="color: #61AFEF">getBalance</span><span style="color: #ABB2BF">() -&gt; </span><span style="color: #E5C07B">Double</span><span style="color: #ABB2BF"> {</span></span><span class="line"><span style="color: #ABB2BF">        </span><span style="color: #C678DD">return</span><span style="color: #ABB2BF"> balance</span></span><span class="line"><span style="color: #ABB2BF">    }</span></span><span class="line"><span style="color: #ABB2BF">}</span></span><span class="line"></span><span class="line"><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> account = </span><span style="color: #61AFEF">BankAccount</span><span style="color: #ABB2BF">(</span><span style="color: #D19A66">5000</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #C678DD">await</span><span style="color: #ABB2BF"> account.</span><span style="color: #61AFEF">deposit</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">amount</span><span style="color: #ABB2BF">: </span><span style="color: #D19A66">100</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> newBalance = </span><span style="color: #C678DD">await</span><span style="color: #ABB2BF"> account.</span><span style="color: #61AFEF">getBalance</span><span style="color: #ABB2BF">()</span></span><span class="line"><span style="color: #56B6C2">print</span><span style="color: #ABB2BF">(</span><span style="color: #98C379">&quot;New Balance: </span><span style="color: #C678DD">\(</span><span style="color: #ABB2BF">newBalance</span><span style="color: #C678DD">)</span><span style="color: #98C379">&quot;</span><span style="color: #ABB2BF">)</span></span></code></pre></div></div></figure><h4 id="Sendable-Types"><a href="#Sendable-Types" class="headerlink" title="Sendable Types"></a><code>Sendable</code> Types</h4><p>The <code>Sendable</code> protocol marks types whose values can be safely passed between concurrency domains (e.g., from one actor to another).</p><ul><li><strong>Automatic Conformance</strong>: Swift’s core value types (<code>Int</code>, <code>String</code>, etc.), <code>struct</code>s and <code>enum</code>s containing only <code>Sendable</code> values, and <code>actor</code> types automatically conform to <code>Sendable</code>.</li><li><strong>Manual Conformance</strong>: A <code>class</code> can conform to <code>Sendable</code> if it is <code>final</code>, all its properties are immutable constants (<code>let</code>), and the types of those properties also conform to <code>Sendable</code>.</li></ul><hr><h2 id="Part-4-Advanced-Specialized-Features"><a href="#Part-4-Advanced-Specialized-Features" class="headerlink" title="Part 4: Advanced &amp; Specialized Features"></a>Part 4: Advanced &amp; Specialized Features</h2><h3 id="1-Property-Observers-and-Wrappers"><a href="#1-Property-Observers-and-Wrappers" class="headerlink" title="1. Property Observers and Wrappers"></a>1. Property Observers and Wrappers</h3><ul><li><p><strong>Property Observers (<code>willSet</code>&#x2F;<code>didSet</code>)</strong>: Execute code before (<code>willSet</code>) or after (<code>didSet</code>) a property’s value is set.</p><figure class="shiki swift"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"><span style="color: #C678DD">struct</span><span style="color: #ABB2BF"> </span><span style="color: #E5C07B">MyStruct</span><span style="color: #ABB2BF"> {</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">var</span><span style="color: #ABB2BF"> myProperty: </span><span style="color: #E5C07B">String</span><span style="color: #ABB2BF"> {</span></span><span class="line"><span style="color: #ABB2BF">        </span><span style="color: #C678DD">willSet</span><span style="color: #ABB2BF">(newName) {</span></span><span class="line"><span style="color: #ABB2BF">            </span><span style="color: #56B6C2">print</span><span style="color: #ABB2BF">(</span><span style="color: #98C379">&quot;Preparing to change from </span><span style="color: #C678DD">\(</span><span style="color: #ABB2BF">myProperty</span><span style="color: #C678DD">)</span><span style="color: #98C379"> to </span><span style="color: #C678DD">\(</span><span style="color: #ABB2BF">newName</span><span style="color: #C678DD">)</span><span style="color: #98C379">&quot;</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #ABB2BF">        }</span></span><span class="line"><span style="color: #ABB2BF">        </span><span style="color: #C678DD">didSet</span><span style="color: #ABB2BF"> {</span></span><span class="line"><span style="color: #ABB2BF">            </span><span style="color: #C678DD">if</span><span style="color: #ABB2BF"> oldValue != myProperty {</span></span><span class="line"><span style="color: #ABB2BF">                </span><span style="color: #56B6C2">print</span><span style="color: #ABB2BF">(</span><span style="color: #98C379">&quot;Value changed from </span><span style="color: #C678DD">\(</span><span style="color: #ABB2BF">oldValue</span><span style="color: #C678DD">)</span><span style="color: #98C379"> to </span><span style="color: #C678DD">\(</span><span style="color: #ABB2BF">myProperty</span><span style="color: #C678DD">)</span><span style="color: #98C379">&quot;</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #ABB2BF">            }</span></span><span class="line"><span style="color: #ABB2BF">        }</span></span><span class="line"><span style="color: #ABB2BF">    }</span></span><span class="line"><span style="color: #ABB2BF">}</span></span></code></pre></div></div></figure></li><li><p><strong>Property Wrappers (<code>@propertyWrapper</code>)</strong>: Encapsulate the storage and logic of a property into a separate type to reduce code duplication.</p><figure class="shiki swift"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"><span style="color: #C678DD">@propertyWrapper</span></span><span class="line"><span style="color: #C678DD">struct</span><span style="color: #ABB2BF"> </span><span style="color: #E5C07B">MyPropertyWrapper</span><span style="color: #ABB2BF">&lt;</span><span style="color: #E5C07B">T</span><span style="color: #ABB2BF">&gt; {</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">private</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">var</span><span style="color: #ABB2BF"> value: T</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">var</span><span style="color: #ABB2BF"> wrappedValue: T {</span></span><span class="line"><span style="color: #ABB2BF">        </span><span style="color: #C678DD">get</span><span style="color: #ABB2BF"> { </span><span style="color: #7F848E; font-style: italic">/* return the value */</span><span style="color: #ABB2BF"> }</span></span><span class="line"><span style="color: #ABB2BF">        </span><span style="color: #C678DD">set</span><span style="color: #ABB2BF"> { </span><span style="color: #7F848E; font-style: italic">/* modify the value */</span><span style="color: #ABB2BF"> }</span></span><span class="line"><span style="color: #ABB2BF">    }</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">init</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">wrappedValue</span><span style="color: #ABB2BF"> </span><span style="color: #ABB2BF; font-style: italic">initialValue</span><span style="color: #ABB2BF">: T) {</span></span><span class="line"><span style="color: #ABB2BF">        </span><span style="color: #E5C07B">self</span><span style="color: #ABB2BF">.value = initialValue</span></span><span class="line"><span style="color: #ABB2BF">    }</span></span><span class="line"><span style="color: #ABB2BF">}</span></span><span class="line"></span><span class="line"><span style="color: #C678DD">struct</span><span style="color: #ABB2BF"> </span><span style="color: #E5C07B">MyExample</span><span style="color: #ABB2BF"> {</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">@MyPropertyWrapper</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">var</span><span style="color: #ABB2BF"> number: </span><span style="color: #E5C07B">Int</span></span><span class="line"><span style="color: #ABB2BF">}</span></span></code></pre></div></div></figure></li></ul><h3 id="2-Key-Paths-and-Dynamic-Member-Lookup"><a href="#2-Key-Paths-and-Dynamic-Member-Lookup" class="headerlink" title="2. Key Paths and Dynamic Member Lookup"></a>2. Key Paths and Dynamic Member Lookup</h3><ul><li><p><strong>Key Paths</strong>: Provide a type-safe way to reference a property of a type. The syntax is <code>\TypeName.propertyName</code>.</p><figure class="shiki swift"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"><span style="color: #C678DD">struct</span><span style="color: #ABB2BF"> </span><span style="color: #E5C07B">BasketballTeam</span><span style="color: #ABB2BF"> {</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">var</span><span style="color: #ABB2BF"> city: </span><span style="color: #E5C07B">String</span></span><span class="line"><span style="color: #ABB2BF">}</span></span><span class="line"><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> cityKeyPath = \BasketballTeam.</span><span style="color: #E06C75">city</span></span><span class="line"><span style="color: #C678DD">var</span><span style="color: #ABB2BF"> team = </span><span style="color: #61AFEF">BasketballTeam</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">city</span><span style="color: #ABB2BF">: </span><span style="color: #98C379">&quot;Boston&quot;</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> teamCity = team[</span><span style="color: #61AFEF">keyPath</span><span style="color: #ABB2BF">: cityKeyPath] </span><span style="color: #7F848E; font-style: italic">// &quot;Boston&quot;</span></span><span class="line"></span><span class="line"><span style="color: #7F848E; font-style: italic">// Simpler syntax in map/filter</span></span><span class="line"><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> names = people.</span><span style="color: #56B6C2">map</span><span style="color: #ABB2BF">(\.</span><span style="color: #E06C75">name</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> adults = people.</span><span style="color: #56B6C2">filter</span><span style="color: #ABB2BF"> { </span><span style="color: #E5C07B">$0</span><span style="color: #ABB2BF">.</span><span style="color: #E06C75">age</span><span style="color: #ABB2BF"> &gt; </span><span style="color: #D19A66">17</span><span style="color: #ABB2BF"> } </span><span style="color: #7F848E; font-style: italic">// Traditional way</span></span><span class="line"><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> adultsWithKeyPath = people.</span><span style="color: #56B6C2">filter</span><span style="color: #ABB2BF"> { </span><span style="color: #E5C07B">$0</span><span style="color: #ABB2BF">[</span><span style="color: #61AFEF">keyPath</span><span style="color: #ABB2BF">: \.</span><span style="color: #E06C75">age</span><span style="color: #ABB2BF">] &gt; </span><span style="color: #D19A66">17</span><span style="color: #ABB2BF"> }</span></span></code></pre></div></div></figure></li><li><p><strong>Dynamic Member Lookup (<code>@dynamicMemberLookup</code>)</strong>: Allows a type to access members dynamically using dot syntax, even if those members are not explicitly defined at compile time.</p><figure class="shiki swift"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"><span style="color: #C678DD">@dynamicMemberLookup</span></span><span class="line"><span style="color: #C678DD">struct</span><span style="color: #ABB2BF"> </span><span style="color: #E5C07B">BaseballTeam</span><span style="color: #ABB2BF"> {</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> city: </span><span style="color: #E5C07B">String</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> nickName: </span><span style="color: #E5C07B">String</span></span><span class="line"><span style="color: #ABB2BF">    </span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">subscript</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">dynamicMember</span><span style="color: #ABB2BF"> </span><span style="color: #ABB2BF; font-style: italic">key</span><span style="color: #ABB2BF">: </span><span style="color: #E5C07B">String</span><span style="color: #ABB2BF">) -&gt; </span><span style="color: #E5C07B">String</span><span style="color: #ABB2BF"> {</span></span><span class="line"><span style="color: #ABB2BF">        </span><span style="color: #C678DD">switch</span><span style="color: #ABB2BF"> key {</span></span><span class="line"><span style="color: #ABB2BF">        </span><span style="color: #C678DD">case</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">&quot;fullname&quot;</span><span style="color: #C678DD">:</span></span><span class="line"><span style="color: #ABB2BF">            </span><span style="color: #C678DD">return</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">&quot;</span><span style="color: #C678DD">\(</span><span style="color: #ABB2BF">city</span><span style="color: #C678DD">)</span><span style="color: #98C379"> </span><span style="color: #C678DD">\(</span><span style="color: #ABB2BF">nickName</span><span style="color: #C678DD">)</span><span style="color: #98C379">&quot;</span></span><span class="line"><span style="color: #ABB2BF">        </span><span style="color: #C678DD">default:</span></span><span class="line"><span style="color: #ABB2BF">            </span><span style="color: #C678DD">return</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">&quot;Unknown&quot;</span></span><span class="line"><span style="color: #ABB2BF">        }</span></span><span class="line"><span style="color: #ABB2BF">    }</span></span><span class="line"><span style="color: #ABB2BF">}</span></span><span class="line"><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> team = </span><span style="color: #61AFEF">BaseballTeam</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">city</span><span style="color: #ABB2BF">: </span><span style="color: #98C379">&quot;Boston&quot;</span><span style="color: #ABB2BF">, </span><span style="color: #61AFEF">nickName</span><span style="color: #ABB2BF">: </span><span style="color: #98C379">&quot;Red Sox&quot;</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #56B6C2">print</span><span style="color: #ABB2BF">(team.</span><span style="color: #E06C75">fullname</span><span style="color: #ABB2BF">) </span><span style="color: #7F848E; font-style: italic">// Prints &quot;Boston Red Sox&quot;</span></span></code></pre></div></div></figure></li></ul><h3 id="3-Custom-Subscripting"><a href="#3-Custom-Subscripting" class="headerlink" title="3. Custom Subscripting"></a>3. Custom Subscripting</h3><p>Allows you to access instances of a type by index, similar to an array or dictionary.</p><ul><li><strong>Basic Subscript</strong>:<figure class="shiki swift"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"><span style="color: #C678DD">class</span><span style="color: #ABB2BF"> </span><span style="color: #E5C07B">MyNames</span><span style="color: #ABB2BF"> {</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">private</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">var</span><span style="color: #ABB2BF"> names = [</span><span style="color: #98C379">&quot;Jon&quot;</span><span style="color: #ABB2BF">, </span><span style="color: #98C379">&quot;Kailey&quot;</span><span style="color: #ABB2BF">, </span><span style="color: #98C379">&quot;Kai&quot;</span><span style="color: #ABB2BF">]</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">subscript</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF; font-style: italic">index</span><span style="color: #ABB2BF">: </span><span style="color: #E5C07B">Int</span><span style="color: #ABB2BF">) -&gt; </span><span style="color: #E5C07B">String</span><span style="color: #ABB2BF"> {</span></span><span class="line"><span style="color: #ABB2BF">        </span><span style="color: #C678DD">get</span><span style="color: #ABB2BF"> { </span><span style="color: #C678DD">return</span><span style="color: #ABB2BF"> names[index] }</span></span><span class="line"><span style="color: #ABB2BF">        </span><span style="color: #C678DD">set</span><span style="color: #ABB2BF"> { names[index] = newValue }</span></span><span class="line"><span style="color: #ABB2BF">    }</span></span><span class="line"><span style="color: #ABB2BF">}</span></span></code></pre></div></div></figure></li><li><strong>Multi-dimensional Subscript</strong>:<figure class="shiki swift"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"><span style="color: #C678DD">struct</span><span style="color: #ABB2BF"> </span><span style="color: #E5C07B">TicTacToe</span><span style="color: #ABB2BF"> {</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">var</span><span style="color: #ABB2BF"> board = [[</span><span style="color: #98C379">&quot;&quot;</span><span style="color: #ABB2BF">,</span><span style="color: #98C379">&quot;&quot;</span><span style="color: #ABB2BF">,</span><span style="color: #98C379">&quot;&quot;</span><span style="color: #ABB2BF">],[</span><span style="color: #98C379">&quot;&quot;</span><span style="color: #ABB2BF">,</span><span style="color: #98C379">&quot;&quot;</span><span style="color: #ABB2BF">,</span><span style="color: #98C379">&quot;&quot;</span><span style="color: #ABB2BF">],[</span><span style="color: #98C379">&quot;&quot;</span><span style="color: #ABB2BF">,</span><span style="color: #98C379">&quot;&quot;</span><span style="color: #ABB2BF">,</span><span style="color: #98C379">&quot;&quot;</span><span style="color: #ABB2BF">]]</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">subscript</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF; font-style: italic">x</span><span style="color: #ABB2BF">: </span><span style="color: #E5C07B">Int</span><span style="color: #ABB2BF">, </span><span style="color: #61AFEF; font-style: italic">y</span><span style="color: #ABB2BF">: </span><span style="color: #E5C07B">Int</span><span style="color: #ABB2BF">) -&gt; </span><span style="color: #E5C07B">String</span><span style="color: #ABB2BF"> {</span></span><span class="line"><span style="color: #ABB2BF">        </span><span style="color: #C678DD">get</span><span style="color: #ABB2BF"> { </span><span style="color: #C678DD">return</span><span style="color: #ABB2BF"> board[x][y] }</span></span><span class="line"><span style="color: #ABB2BF">        </span><span style="color: #C678DD">set</span><span style="color: #ABB2BF"> { board[x][y] = newValue }</span></span><span class="line"><span style="color: #ABB2BF">    }</span></span><span class="line"><span style="color: #ABB2BF">}</span></span><span class="line"><span style="color: #C678DD">var</span><span style="color: #ABB2BF"> board = </span><span style="color: #61AFEF">TicTacToe</span><span style="color: #ABB2BF">()</span></span><span class="line"><span style="color: #ABB2BF">board[</span><span style="color: #D19A66">1</span><span style="color: #ABB2BF">, </span><span style="color: #D19A66">1</span><span style="color: #ABB2BF">] = </span><span style="color: #98C379">&quot;x&quot;</span></span></code></pre></div></div></figure></li><li><strong>Static Subscript</strong>:<figure class="shiki swift"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"><span style="color: #C678DD">struct</span><span style="color: #ABB2BF"> </span><span style="color: #E5C07B">Hello</span><span style="color: #ABB2BF"> {</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">static</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">subscript</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF; font-style: italic">name</span><span style="color: #ABB2BF">: </span><span style="color: #E5C07B">String</span><span style="color: #ABB2BF">) -&gt; </span><span style="color: #E5C07B">String</span><span style="color: #ABB2BF"> {</span></span><span class="line"><span style="color: #ABB2BF">        </span><span style="color: #C678DD">return</span><span style="color: #ABB2BF"> </span><span style="color: #98C379">&quot;Hello </span><span style="color: #C678DD">\(</span><span style="color: #ABB2BF">name</span><span style="color: #C678DD">)</span><span style="color: #98C379">&quot;</span></span><span class="line"><span style="color: #ABB2BF">    }</span></span><span class="line"><span style="color: #ABB2BF">}</span></span><span class="line"><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> greeting = Hello[</span><span style="color: #98C379">&quot;Jon&quot;</span><span style="color: #ABB2BF">]</span></span></code></pre></div></div></figure></li></ul><h3 id="4-Result-Builders"><a href="#4-Result-Builders" class="headerlink" title="4. Result Builders"></a>4. Result Builders</h3><p>Result builders are a special syntax transformation that lets you build a complex result from a sequence of statements, commonly used for creating Domain-Specific Languages (DSLs), like SwiftUI’s view builder.</p><figure class="shiki swift"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"><span style="color: #C678DD">@resultBuilder</span></span><span class="line"><span style="color: #C678DD">struct</span><span style="color: #ABB2BF"> </span><span style="color: #E5C07B">StringBuilder</span><span style="color: #ABB2BF"> {</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">static</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">func</span><span style="color: #ABB2BF"> </span><span style="color: #61AFEF">buildBlock</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">_</span><span style="color: #ABB2BF"> </span><span style="color: #ABB2BF; font-style: italic">components</span><span style="color: #ABB2BF">: </span><span style="color: #E5C07B">String</span><span style="color: #ABB2BF">...) -&gt; </span><span style="color: #E5C07B">String</span><span style="color: #ABB2BF"> {</span></span><span class="line"><span style="color: #ABB2BF">        </span><span style="color: #C678DD">return</span><span style="color: #ABB2BF"> components.</span><span style="color: #56B6C2">joined</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">separator</span><span style="color: #ABB2BF">: </span><span style="color: #98C379">&quot; &quot;</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #ABB2BF">    }</span></span><span class="line"><span style="color: #ABB2BF">}</span></span><span class="line"></span><span class="line"><span style="color: #C678DD">func</span><span style="color: #ABB2BF"> </span><span style="color: #61AFEF">buildString</span><span style="color: #ABB2BF">(@StringBuilder </span><span style="color: #61AFEF">_</span><span style="color: #ABB2BF"> </span><span style="color: #ABB2BF; font-style: italic">components</span><span style="color: #ABB2BF">: () -&gt; </span><span style="color: #E5C07B">String</span><span style="color: #ABB2BF">) -&gt; </span><span style="color: #E5C07B">String</span><span style="color: #ABB2BF"> {</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">return</span><span style="color: #ABB2BF"> </span><span style="color: #61AFEF">components</span><span style="color: #ABB2BF">()</span></span><span class="line"><span style="color: #ABB2BF">}</span></span><span class="line"></span><span class="line"><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> result = </span><span style="color: #61AFEF">buildString</span><span style="color: #ABB2BF"> {</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #98C379">&quot;Hello,&quot;</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #98C379">&quot;Mastering&quot;</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #98C379">&quot;Swift!&quot;</span></span><span class="line"><span style="color: #ABB2BF">}</span></span><span class="line"><span style="color: #56B6C2">print</span><span style="color: #ABB2BF">(result) </span><span style="color: #7F848E; font-style: italic">// &quot;Hello, Mastering Swift!&quot;</span></span></code></pre></div></div></figure><h3 id="5-Reflection"><a href="#5-Reflection" class="headerlink" title="5. Reflection"></a>5. Reflection</h3><p>Swift’s <code>Mirror</code> API allows you to inspect the properties, types, and values of an instance at runtime. Swift’s reflection is read-only, in keeping with its principle of type safety.</p><figure class="shiki swift"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> person = </span><span style="color: #61AFEF">Person</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">firstName</span><span style="color: #ABB2BF">: </span><span style="color: #98C379">&quot;Jon&quot;</span><span style="color: #ABB2BF">, </span><span style="color: #61AFEF">lastName</span><span style="color: #ABB2BF">: </span><span style="color: #98C379">&quot;Hoffman&quot;</span><span style="color: #ABB2BF">, </span><span style="color: #61AFEF">age</span><span style="color: #ABB2BF">: </span><span style="color: #D19A66">55</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> mirror = </span><span style="color: #E5C07B">Mirror</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">reflecting</span><span style="color: #ABB2BF">: person)</span></span><span class="line"></span><span class="line"><span style="color: #C678DD">for</span><span style="color: #ABB2BF"> (label, value) </span><span style="color: #C678DD">in</span><span style="color: #ABB2BF"> mirror.children {</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #56B6C2">print</span><span style="color: #ABB2BF">(</span><span style="color: #98C379">&quot;Property: </span><span style="color: #C678DD">\(</span><span style="color: #ABB2BF">label ?? </span><span style="color: #98C379">&quot;Unknown&quot;</span><span style="color: #C678DD">)</span><span style="color: #98C379">, Value: </span><span style="color: #C678DD">\(</span><span style="color: #ABB2BF">value</span><span style="color: #C678DD">)</span><span style="color: #98C379">&quot;</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #ABB2BF">}</span></span><span class="line"></span><span class="line"><span style="color: #7F848E; font-style: italic">// Can be used to implement a generic serialization function</span></span><span class="line"><span style="color: #C678DD">func</span><span style="color: #ABB2BF"> </span><span style="color: #61AFEF">serialize</span><span style="color: #ABB2BF">&lt;</span><span style="color: #E5C07B">T</span><span style="color: #ABB2BF">&gt;(</span><span style="color: #61AFEF">_</span><span style="color: #ABB2BF"> </span><span style="color: #ABB2BF; font-style: italic">value</span><span style="color: #ABB2BF">: T) -&gt; [</span><span style="color: #E5C07B">String</span><span style="color: #ABB2BF">: Any] {</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> mirror = </span><span style="color: #E5C07B">Mirror</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">reflecting</span><span style="color: #ABB2BF">: value)</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">var</span><span style="color: #ABB2BF"> result = [</span><span style="color: #E5C07B">String</span><span style="color: #C678DD">:</span><span style="color: #ABB2BF"> Any]()</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">for</span><span style="color: #ABB2BF"> child </span><span style="color: #C678DD">in</span><span style="color: #ABB2BF"> mirror.children {</span></span><span class="line"><span style="color: #ABB2BF">        </span><span style="color: #C678DD">if</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> propertyName = child.label {</span></span><span class="line"><span style="color: #ABB2BF">            result[propertyName] = child.value</span></span><span class="line"><span style="color: #ABB2BF">        }</span></span><span class="line"><span style="color: #ABB2BF">    }</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">return</span><span style="color: #ABB2BF"> result</span></span><span class="line"><span style="color: #ABB2BF">}</span></span></code></pre></div></div></figure><h3 id="6-Regular-Expressions"><a href="#6-Regular-Expressions" class="headerlink" title="6. Regular Expressions"></a>6. Regular Expressions</h3><p>Swift provides modern and powerful support for regular expressions.</p><ul><li><strong>Literal Syntax</strong>:<figure class="shiki swift"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> pattern = </span><span style="color: #E06C75">/</span><span style="color: #C678DD">\b</span><span style="color: #D19A66">\w+</span><span style="color: #C678DD">\b</span><span style="color: #E06C75">/</span></span><span class="line"><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> text = </span><span style="color: #98C379">&quot;Hello from regex literal&quot;</span></span><span class="line"><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> matches = text.</span><span style="color: #61AFEF">matches</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">of</span><span style="color: #ABB2BF">: pattern)</span></span><span class="line"><span style="color: #C678DD">for</span><span style="color: #ABB2BF"> match </span><span style="color: #C678DD">in</span><span style="color: #ABB2BF"> matches {</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #56B6C2">print</span><span style="color: #ABB2BF">(</span><span style="color: #98C379">&quot;-- </span><span style="color: #C678DD">\(</span><span style="color: #ABB2BF">text[match.range]</span><span style="color: #C678DD">)</span><span style="color: #98C379">&quot;</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #ABB2BF">}</span></span></code></pre></div></div></figure></li><li><strong><code>RegexBuilder</code></strong>: Construct complex regular expressions in a declarative way.<figure class="shiki swift"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"><span style="color: #C678DD">import</span><span style="color: #ABB2BF"> </span><span style="color: #E5C07B">RegexBuilder</span></span><span class="line"></span><span class="line"><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> pattern = </span><span style="color: #61AFEF">Regex</span><span style="color: #ABB2BF"> {</span></span><span class="line"><span style="color: #ABB2BF">    Anchor.</span><span style="color: #E06C75">wordBoundary</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #61AFEF">OneOrMore</span><span style="color: #ABB2BF">(.</span><span style="color: #E06C75">word</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #98C379">&quot;@&quot;</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #7F848E; font-style: italic">// ... more components</span></span><span class="line"><span style="color: #ABB2BF">}</span></span></code></pre></div></div></figure></li><li><strong>Capturing</strong>: You can define references to capture matched parts and perform type conversions.<figure class="shiki swift"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> animalTypeRef = </span><span style="color: #61AFEF">Reference</span><span style="color: #ABB2BF">(Substring.</span><span style="color: #C678DD">self</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> ageRef = </span><span style="color: #61AFEF">Reference</span><span style="color: #ABB2BF">(</span><span style="color: #E5C07B">Int</span><span style="color: #ABB2BF">.</span><span style="color: #C678DD">self</span><span style="color: #ABB2BF">)</span></span><span class="line"></span><span class="line"><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> pattern = </span><span style="color: #61AFEF">Regex</span><span style="color: #ABB2BF"> {</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #98C379">&quot;I am a &quot;</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #61AFEF">Capture</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">as</span><span style="color: #ABB2BF">: animalTypeRef) { </span><span style="color: #61AFEF">OneOrMore</span><span style="color: #ABB2BF">(.</span><span style="color: #E06C75">word</span><span style="color: #ABB2BF">) }</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #98C379">&quot; who is &quot;</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #61AFEF">TryCapture</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">as</span><span style="color: #ABB2BF">: ageRef) { </span><span style="color: #61AFEF">OneOrMore</span><span style="color: #ABB2BF">(.</span><span style="color: #E06C75">digit</span><span style="color: #ABB2BF">) } </span><span style="color: #61AFEF">transform</span><span style="color: #ABB2BF">: { </span><span style="color: #E5C07B">Int</span><span style="color: #ABB2BF">(</span><span style="color: #E5C07B">$0</span><span style="color: #ABB2BF">) }</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #7F848E; font-style: italic">// ...</span></span><span class="line"><span style="color: #ABB2BF">}</span></span><span class="line"></span><span class="line"><span style="color: #C678DD">if</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> match = str.</span><span style="color: #61AFEF">firstMatch</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">of</span><span style="color: #ABB2BF">: pattern) {</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #56B6C2">print</span><span style="color: #ABB2BF">(</span><span style="color: #98C379">&quot;Animal Type: </span><span style="color: #C678DD">\(</span><span style="color: #ABB2BF">match[animalTypeRef]</span><span style="color: #C678DD">)</span><span style="color: #98C379">&quot;</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #56B6C2">print</span><span style="color: #ABB2BF">(</span><span style="color: #98C379">&quot;Age: </span><span style="color: #C678DD">\(</span><span style="color: #ABB2BF">match[ageRef]</span><span style="color: #C678DD">)</span><span style="color: #98C379">&quot;</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #ABB2BF">}</span></span></code></pre></div></div></figure></li></ul><hr><h2 id="Part-5-Code-Quality-Organization"><a href="#Part-5-Code-Quality-Organization" class="headerlink" title="Part 5: Code Quality &amp; Organization"></a>Part 5: Code Quality &amp; Organization</h2><h3 id="1-Access-Control"><a href="#1-Access-Control" class="headerlink" title="1. Access Control"></a>1. Access Control</h3><p>Access control restricts access to parts of your code from code in other source files and modules.</p><ul><li><code>open</code>: The most permissive. Allows access and subclassing from any module (for classes only).</li><li><code>public</code>: Allows access from any module.</li><li><code>internal</code>: The default level. Allows access only within the defining module.</li><li><code>fileprivate</code>: Allows access only within the defining source file.</li><li><code>private</code>: The most restrictive. Allows access only within the enclosing declaration (like a <code>struct</code> or <code>class</code>).</li></ul><p><strong>Best Practices</strong>:</p><ul><li><strong>Default to the most restrictive access level</strong> and relax it as needed.</li><li><strong>Encapsulate implementation details</strong> and expose only the necessary API.</li><li><strong>Maintain consistency</strong> by following a uniform access control strategy throughout your codebase.</li></ul><h3 id="2-Availability-Checks"><a href="#2-Availability-Checks" class="headerlink" title="2. Availability Checks"></a>2. Availability Checks</h3><p>Use <code>#available</code> to check the operating system version and execute different code accordingly.</p><figure class="shiki swift"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"><span style="color: #C678DD">if</span><span style="color: #ABB2BF"> </span><span style="color: #56B6C2">#available</span><span style="color: #ABB2BF">(</span><span style="color: #C678DD">iOS</span><span style="color: #ABB2BF"> </span><span style="color: #D19A66">16.0</span><span style="color: #ABB2BF">, </span><span style="color: #C678DD">macOS</span><span style="color: #ABB2BF"> </span><span style="color: #D19A66">13.0</span><span style="color: #ABB2BF">, </span><span style="color: #C678DD">*</span><span style="color: #ABB2BF">) {</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #7F848E; font-style: italic">// Use new APIs for iOS 16+ and macOS 13+</span></span><span class="line"><span style="color: #ABB2BF">} </span><span style="color: #C678DD">else</span><span style="color: #ABB2BF"> {</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #7F848E; font-style: italic">// Provide a fallback for older versions</span></span><span class="line"><span style="color: #ABB2BF">}</span></span></code></pre></div></div></figure><p>You can also mark an entire function or type with <code>@available</code>.</p><figure class="shiki swift"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"><span style="color: #C678DD">@available</span><span style="color: #ABB2BF">(</span><span style="color: #C678DD">iOS</span><span style="color: #ABB2BF"> </span><span style="color: #D19A66">16.0</span><span style="color: #ABB2BF">, </span><span style="color: #C678DD">*</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #C678DD">func</span><span style="color: #ABB2BF"> </span><span style="color: #61AFEF">newFeature</span><span style="color: #ABB2BF">() {</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #7F848E; font-style: italic">// This function is only available on iOS 16 and later</span></span><span class="line"><span style="color: #ABB2BF">}</span></span></code></pre></div></div></figure><h3 id="3-Swift-Testing"><a href="#3-Swift-Testing" class="headerlink" title="3. Swift Testing"></a>3. Swift Testing</h3><p>Swift provides the <code>Testing</code> framework, a modern and expressive solution for testing.</p><ul><li><p><strong><code>@Test</code></strong>: Marks a function as a test function.</p></li><li><p><strong><code>#expect</code> and <code>#require</code></strong>:</p><ul><li><code>#expect</code>: Checks if a condition is true. If it fails, the test continues to run.</li><li><code>#require</code>: Checks if a condition is true. If it fails, the test terminates immediately.</li></ul><!-- end list --><figure class="shiki swift"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"><span style="color: #C678DD">@Test</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">func</span><span style="color: #ABB2BF"> </span><span style="color: #61AFEF">validExpectation</span><span style="color: #ABB2BF">() </span><span style="color: #C678DD">throws</span><span style="color: #ABB2BF"> {</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #61AFEF">#expect</span><span style="color: #ABB2BF">(</span><span style="color: #D19A66">1</span><span style="color: #ABB2BF"> == </span><span style="color: #D19A66">1</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> one: </span><span style="color: #E5C07B">Int</span><span style="color: #ABB2BF">? = </span><span style="color: #D19A66">10</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> willSucceed = </span><span style="color: #C678DD">try</span><span style="color: #ABB2BF"> </span><span style="color: #61AFEF">#require</span><span style="color: #ABB2BF">(one) </span><span style="color: #7F848E; font-style: italic">// Succeeds, test continues</span></span><span class="line"><span style="color: #ABB2BF">}</span></span></code></pre></div></div></figure></li><li><p><strong>Test Suites (<code>@Suite</code>)</strong>: Used to organize related tests. Any <code>struct</code> containing <code>@Test</code> functions automatically becomes a suite.</p><figure class="shiki swift"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"><span style="color: #C678DD">@Suite</span><span style="color: #ABB2BF">(</span><span style="color: #98C379">&quot;Calculator Tests&quot;</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #C678DD">struct</span><span style="color: #ABB2BF"> </span><span style="color: #E5C07B">CalculatorTests</span><span style="color: #ABB2BF"> {</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #C678DD">@Test</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">func</span><span style="color: #ABB2BF"> </span><span style="color: #61AFEF">testAddition</span><span style="color: #ABB2BF">() { </span><span style="color: #7F848E; font-style: italic">/* ... */</span><span style="color: #ABB2BF"> }</span></span><span class="line"><span style="color: #ABB2BF">}</span></span></code></pre></div></div></figure></li><li><p><strong>Parameterized Tests</strong>: Provide multiple sets of inputs and expected outputs for a single test.</p><figure class="shiki swift"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"><span style="color: #C678DD">struct</span><span style="color: #ABB2BF"> </span><span style="color: #E5C07B">TestValues</span><span style="color: #ABB2BF"> { </span><span style="color: #C678DD">let</span><span style="color: #ABB2BF"> first: </span><span style="color: #E5C07B">Double</span><span style="color: #ABB2BF">, second: </span><span style="color: #E5C07B">Double</span><span style="color: #ABB2BF">, answer: </span><span style="color: #E5C07B">Double</span><span style="color: #ABB2BF"> }</span></span><span class="line"></span><span class="line"><span style="color: #C678DD">@Test</span><span style="color: #ABB2BF">(</span><span style="color: #98C379">&quot;Addition Tests&quot;</span><span style="color: #ABB2BF">, arguments</span><span style="color: #C678DD">:</span><span style="color: #ABB2BF"> [</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #61AFEF">TestValues</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">first</span><span style="color: #ABB2BF">: </span><span style="color: #D19A66">2</span><span style="color: #ABB2BF">, </span><span style="color: #61AFEF">second</span><span style="color: #ABB2BF">: </span><span style="color: #D19A66">3</span><span style="color: #ABB2BF">, </span><span style="color: #61AFEF">answer</span><span style="color: #ABB2BF">: </span><span style="color: #D19A66">5</span><span style="color: #ABB2BF">),</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #61AFEF">TestValues</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">first</span><span style="color: #ABB2BF">: </span><span style="color: #D19A66">10</span><span style="color: #ABB2BF">, </span><span style="color: #61AFEF">second</span><span style="color: #ABB2BF">: </span><span style="color: #D19A66">11</span><span style="color: #ABB2BF">, </span><span style="color: #61AFEF">answer</span><span style="color: #ABB2BF">: </span><span style="color: #D19A66">21</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #ABB2BF">])</span></span><span class="line"><span style="color: #C678DD">func</span><span style="color: #ABB2BF"> </span><span style="color: #61AFEF">testAddition</span><span style="color: #ABB2BF">(</span><span style="color: #61AFEF">_</span><span style="color: #ABB2BF"> </span><span style="color: #ABB2BF; font-style: italic">values</span><span style="color: #ABB2BF">: TestValues) {</span></span><span class="line"><span style="color: #ABB2BF">    </span><span style="color: #61AFEF">#expect</span><span style="color: #ABB2BF">(Calculator.</span><span style="color: #61AFEF">addition</span><span style="color: #ABB2BF">(values.first, values.</span><span style="color: #E06C75">second</span><span style="color: #ABB2BF">) == values.</span><span style="color: #E06C75">answer</span><span style="color: #ABB2BF">)</span></span><span class="line"><span style="color: #ABB2BF">}</span></span></code></pre></div></div></figure></li><li><p><strong><code>@testable</code></strong>: Imports a module and allows test code to access its <code>internal</code> members.</p><figure class="shiki swift"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"><span style="color: #C678DD">@testable</span><span style="color: #ABB2BF"> </span><span style="color: #C678DD">import</span><span style="color: #ABB2BF"> </span><span style="color: #E5C07B">MyApp</span></span></code></pre></div></div></figure></li></ul>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;I am ready to systematically review the iOS knowledge, so I have this simple Swift 6 Programming study notes.&lt;/p&gt;
&lt;h2 id=&quot;Part-1-Core-Con</summary>
      
    
    
    
    <category term="Dev" scheme="https://ihugo.cc/categories/Dev/"/>
    
    
    <category term="iOS" scheme="https://ihugo.cc/tags/iOS/"/>
    
    <category term="Life/Meta" scheme="https://ihugo.cc/tags/Life-Meta/"/>
    
  </entry>
  
  <entry>
    <title>prepare-det</title>
    <link href="https://ihugo.cc/2025/04/08/000-prepare-det/"/>
    <id>https://ihugo.cc/2025/04/08/000-prepare-det/</id>
    <published>2025-04-08T00:54:27.000Z</published>
    <updated>2026-05-25T03:24:31.409Z</updated>
    
    <content type="html"><![CDATA[<p>Today I start to prepare Duolingo English Test. I made honey sweet potato chips for the children today, and the children loved it.</p><p><img src="/img/prepare-det.assets/hongshu.png" alt="honey sweet potato chips"></p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;Today I start to prepare Duolingo English Test. I made honey sweet potato chips for the children today, and the children loved it.&lt;/p&gt;
&lt;p</summary>
      
    
    
    
    <category term="Life" scheme="https://ihugo.cc/categories/Life/"/>
    
    
    <category term="det" scheme="https://ihugo.cc/tags/det/"/>
    
  </entry>
  
  <entry>
    <title>Modern Vim: Tips and Techniques for Efficient Editing</title>
    <link href="https://ihugo.cc/2023/07/13/000-Modern-Vim-Tips-and-Techniques-for-Efficient-Editing/"/>
    <id>https://ihugo.cc/2023/07/13/000-Modern-Vim-Tips-and-Techniques-for-Efficient-Editing/</id>
    <published>2023-07-13T01:09:10.000Z</published>
    <updated>2026-05-25T03:24:31.408Z</updated>
    
    <content type="html"><![CDATA[<p>Vim is a powerful and versatile text editor that offers a wide range of features and customization options. In this article, we will explore some useful tips and techniques from the book “Modern Vim” to help you become more efficient and productive in your Vim editing.</p><p><img src="/img/Modern-Vim-Tips-and-Techniques-for-Efficient-Editing.assets/1*huYiLzIRleQc8_8OxvHVlQ.png" alt="img"></p><h2 id="Introduction"><a href="#Introduction" class="headerlink" title="Introduction"></a>Introduction</h2><p>Vim has a unique set of keyboard shortcuts and commands that can enhance your editing experience. Here are some key concepts and shortcuts to keep in mind:</p><ul><li><p><code>&lt;C-p&gt;</code> represents pressing the Control key followed by the letter “p.”</p></li><li><p>Operators are commands used to perform actions on text. Some common operators include:</p>  <figure class="shiki"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"><span style="color: #abb2bf">`c` for change</span></span><span class="line"><span style="color: #abb2bf">`d` for delete</span></span><span class="line"><span style="color: #abb2bf">`y` for yank (copy)</span></span><span class="line"><span style="color: #abb2bf">`~` for swapping case</span></span><span class="line"><span style="color: #abb2bf">`gu` for making text lowercase</span></span><span class="line"><span style="color: #abb2bf">`gU` for making text uppercase</span></span><span class="line"><span style="color: #abb2bf">`!` for filtering text through an external program</span></span><span class="line"><span style="color: #abb2bf">`=` for text formatting</span></span><span class="line"><span style="color: #abb2bf">`gq` for text formatting with line wrapping</span></span><span class="line"><span style="color: #abb2bf">`&gt;` for shifting text right</span></span><span class="line"><span style="color: #abb2bf">`&lt;` for shifting text left</span></span><span class="line"><span style="color: #abb2bf">`zf` for defining a fold</span></span><span class="line"><span style="color: #abb2bf">`g@` for calling a function set with the &#39;operatorfunc&#39; option</span></span></code></pre></div></div></figure></li><li><p>The <code>Meta</code> key:</p><ul><li>On macOS, it refers to the Option key.</li><li>On Windows, it refers to the Alt key.</li></ul></li></ul><h2 id="Getting-Modern-Vim"><a href="#Getting-Modern-Vim" class="headerlink" title="Getting Modern Vim"></a>Getting Modern Vim</h2><p>To get the most out of Vim, it’s recommended to use Neovim and the neovim-remote tool developed by Marco Hinz. This tool allows remote control of Neovim processes.</p><h2 id="Installing-Plugins"><a href="#Installing-Plugins" class="headerlink" title="Installing Plugins"></a>Installing Plugins</h2><p>Plugins are a great way to extend Vim’s functionality. Here are some insights on managing plugins:</p><ul><li><p>Understanding Scripts, Plugins, and Packages:</p><ul><li>You can manually load a script using the <code>:source {path}</code> command, where <code>{path}</code> is the location of the script.</li><li>Vim automatically sources scripts located in specific locations on disk when it starts up.</li><li>Your <code>vimrc</code> file is one of the first scripts to be loaded, making it an ideal place to configure your startup settings.</li><li>Prior to recent versions of Vim, managing the <code>runtimepath</code> to include plugins was not convenient. However, you can now use the <code>:set runtimepath+=$VIMCONFIG/arbitrary/demo-plugin</code> command to add a plugin to the <code>runtimepath</code>.</li><li>Pressing <code>&lt;C-]&gt;</code> will jump to the specified anchor in Vim’s documentation, and you can use <code>&lt;C-o&gt;</code> to quickly jump back to the previous location. These commands allow you to navigate Vim’s documentation similar to interacting with a web page.</li><li>After installing a new plugin, you only need to run <code>:helptags</code> once. Vim will then use the generated tags file to find the documentation for that plugin.</li></ul></li><li><p>Installing Plugins to Your Package:</p><ul><li>Note that if you install a new plugin into the <code>start</code> directory while Vim is running, you won’t be able to use it immediately. Restarting Vim will add the new plugin to the <code>runtimepath</code> and make it available.</li><li>The unimpaired plugin comes with documentation, but Vim doesn’t know where to find the appropriate files. You can fix this issue by running the <code>:helptags ALL</code> command (<code>:help :helptags</code>).</li><li>You can suppress error messages by running <code>:silent helptags ALL</code>.</li><li>By default, optional plugins are not loaded. Use the <code>:packadd</code> command to activate a plugin (e.g., <code>:packadd vim-scriptease</code>).</li></ul></li><li><p>Managing Plugins with minpac:</p><ul><li>Typing <code>:call minpac#update()</code> can be cumbersome. You can create custom commands to make it more convenient:<ul><li><code>command! PackUpdate call minpac#update()</code></li><li><code>command! PackClean call minpac#clean()</code></li></ul></li></ul></li></ul><h2 id="Opening-Files"><a href="#Opening-Files" class="headerlink" title="Opening Files"></a>Opening Files</h2><p>Efficiently opening and navigating files is crucial for an effective editing workflow. Let’s explore two techniques:</p><ul><li><p>Finding Files Using Fuzzy Path Matching:</p><ul><li>You can use <code>&lt;C-x&gt;</code>, <code>&lt;C-v&gt;</code>, or <code>&lt;C-t&gt;</code> to open a file in a horizontal split, vertical split, or new tab, respectively.</li><li>The <code>rg --files</code> command (Ripgrep) filters out files ignored by Git, Mercurial, and Subversion repositories.</li></ul></li><li><p>Finding Files Semantically:</p><ul><li><p>Open files in separate windows using the <code>-O</code> flag. For example:</p><ul><li><code>vim -O file1.txt file2.txt</code></li></ul></li><li><p>You can define file-to-type mappings in a <code>.projections.json</code> file. For instance:</p><ul><li><code>&quot;app/models/*.js&quot;: { &quot;type&quot;: &quot;model&quot; }</code></li></ul></li><li><p>Vim provides navigation commands specific to file types, such as:</p><ul><li><figure class="shiki"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"><span style="color: #abb2bf">`:Etype` - Opens the specified type in the current window</span></span><span class="line"><span style="color: #abb2bf">`:Stype` - Opens the specified type in a horizontal split</span></span><span class="line"><span style="color: #abb2bf">`:Vtype` - Opens the specified type in a vertical split</span></span><span class="line"><span style="color: #abb2bf">`:Ttype` - Opens the specified type in a new tabpage</span></span></code></pre></div></div></figure></li></ul></li><li><p>These navigation commands are my preferred way to navigate codebases, and you can add navigation commands as needed for different file types.</p></li></ul></li><li><p>Jumping to an Alternate File:</p><ul><li>The Projectionist plugin allows you to create links between related files. Once you specify the relationship between a file and its alternate file, you can follow the link by running the <code>:A</code> command.</li></ul></li></ul><h2 id="Working-with-the-Quickfix-List"><a href="#Working-with-the-Quickfix-List" class="headerlink" title="Working with the Quickfix List"></a>Working with the Quickfix List</h2><p>The quickfix list is a powerful feature in Vim that allows you to efficiently handle build errors, linting results, and file search results. Let’s explore some techniques:</p><ul><li>Running a Build and Navigating Failures:<ul><li>The Dispatch plugin, introduced in 2013, provides asynchronous command execution in Vim when it didn’t natively support it. Make sure to check out the plugin’s release and the “dispatch.vim” screencast.</li></ul></li><li>Linting the Current File:<ul><li>Learn about the Asynchronous Linting Engine (ALE), a powerful plugin for linting code in Vim.</li><li>You can use the <code>]w</code> and <code>[w</code> mappings to quickly navigate between warnings. Error messages are displayed at the bottom of the screen as you access each warning.</li><li>Neomake is another linting plugin that runs asynchronously. It supports running commands across the entire project, not just on individual files.</li></ul></li><li>Searching Files with Grep-Alikes:<ul><li>The <code>:Grepper</code> command provides a powerful way to search for patterns in files. For example, running <code>:Grepper -cword</code> with the word “Waldo” under the cursor will prompt you for search options.</li></ul></li></ul><h2 id="Neovim’s-Built-In-Terminal-Emulator"><a href="#Neovim’s-Built-In-Terminal-Emulator" class="headerlink" title="Neovim’s Built-In Terminal Emulator"></a>Neovim’s Built-In Terminal Emulator</h2><p>Neovim comes with a built-in terminal emulator that allows you to interact with programs running in the terminal. Let’s dive into some terminal-related techniques:</p><ul><li>Grokking Terminal Mode:<ul><li>Neovim introduces a new mode called Terminal mode, where you can interact with programs running in the built-in terminal emulator.</li><li>Use the <code>:terminal</code> command to open a terminal buffer.</li><li>When you create a terminal buffer, you start in normal mode. Pressing <code>i</code> switches to terminal mode, indicated by the</li></ul></li></ul><p> <code>-- TERMINAL --</code> prompt in the bottom left corner. Press <code>&lt;C-&gt;&lt;C-n&gt;</code> to switch back to normal mode.</p><ul><li><p>Running Programs in a Terminal Buffer:</p><ul><li>Use the <code>:read !{cmd}</code> command to capture the output of a command in an existing buffer.</li><li>The <code>:terminal {cmd}</code> command is a new feature in Neovim. It runs the specified command in a new terminal buffer. You can abbreviate it as <code>:te {cmd}</code>.</li><li>To switch between a terminal buffer and a regular buffer, use <code>&lt;C-^&gt;</code> (<code>:h ctrl-^</code>).</li><li>Try stopping a process in the terminal buffer using the <code>:bwipeout!</code> command (e.g., <code>:5bwipeout</code> to stop the top process).</li><li>When you exit Neovim, any running processes in terminal buffers are also closed.</li><li>Note that if you suspend Neovim (<code>&lt;C-z&gt;</code>), all processes running in terminal buffers will be suspended as well. They will resume when you resume Neovim.</li></ul></li><li><p>Managing Windows That Contain Terminal Buffers:</p><ul><li>Opening a terminal buffer with the <code>:terminal</code> command takes over the current window and hides the buffer that was previously displayed. This behavior is similar to the <code>:edit {file}</code> command.</li><li>If you use <code>:te</code>, it creates a buffer. However, <code>:te</code> does not create a buffer.</li><li>Use the <code>tnoremap</code> command to create mappings that work only in terminal mode. With these mappings, you can switch to another window by pressing <code>&lt;M-h&gt;</code>, <code>&lt;M-j&gt;</code>, <code>&lt;M-k&gt;</code>, or <code>&lt;M-l&gt;</code> regardless of whether you are in normal mode or terminal mode.</li></ul></li><li><p>Using Normal Mode Commands in a Terminal Buffer:</p><ul><li>You can use the <code>yi`` command to copy the text within backticks to Vim&#39;s unnamed register and then paste it using </code>p&#96; in the terminal at the cursor position.</li><li>You can prepend a named register (e.g., <code>&quot;a</code>) or use special registers like <code>&quot;*</code> to reference the system clipboard when using yank and put commands.</li><li>The terminal buffer is now hidden, but you can quickly switch back to it using <code>&lt;C-^&gt;</code> (<code>:h ctrl-^</code>).</li></ul></li><li><p>Sending Commands to a Terminal Buffer:</p><ul><li>Activate the window containing the terminal buffer running the web server and run the following command:<ul><li><code>:echo b:terminal_job_id</code></li><li>This tells us that the job ID is 1, which we can use as the first argument when calling <code>jobsend({job}, {data})</code>.</li><li>To restart the web server, run the command:<ul><li><code>:call jobsend(1, &quot;\&lt;C-c&gt;npm run server\&lt;CR&gt;&quot;)</code></li></ul></li></ul></li></ul></li></ul><h2 id="Sessions"><a href="#Sessions" class="headerlink" title="Sessions"></a>Sessions</h2><p>Sessions in Vim allow you to save and restore your editing environment. Let’s explore session-related techniques:</p><ul><li><p>Saving and Restoring Sessions:</p><ul><li>After opening the <code>app.js</code> and <code>app-test.js</code> files in adjacent windows, use the <code>:mksession!</code> command (<code>:h :mksession</code>) to save the session.</li><li>Restart Vim with the <code>-S</code> flag to load the session:<ul><li><code>vim -S</code></li></ul></li><li>If you like the idea of automatically recording sessions, consider installing Tim Pope’s Obsession plugin. You can install it in your bundle directory:<ul><li><code>cd $VIMCONFIG/pack/bundle/start</code></li><li><code>git clone https://github.com/tpope/vim-obsession.git</code></li></ul></li></ul></li><li><p>Making Undo Persist Between Sessions:</p><ul><li>By default, undo history is not preserved between sessions. However, you can use autocommands to disable the undofile for files matching specific patterns. For example, the following is an example script that disables persistent undo in all files in the <code>/tmp</code> directory:<ul><li><code>--forget-undo-in-tmpfile.vim</code></li><li><figure class="shiki"><div class='codeblock'><div class="code"><pre class="shiki one-dark-pro"><code><span class="line"><span style="color: #abb2bf">augroup vimrc</span></span><span class="line"><span style="color: #abb2bf">autocmd!</span></span><span class="line"><span style="color: #abb2bf">autocmd BufWritePre /tmp/* setlocal noundofile</span></span><span class="line"><span style="color: #abb2bf">augroup END</span></span></code></pre></div></div></figure></li></ul></li></ul></li><li><p>Restarting Terminal Processes When Resuming a Session:</p><ul><li>You can rename a terminal buffer using the <code>:file {name}</code> command (<code>:help :file_f</code>). Activate the window containing the terminal buffer running the web server and run:<ul><li><code>:file term://PORT=3001 npm run server</code></li></ul></li></ul></li></ul><h2 id="Configuring-Vim"><a href="#Configuring-Vim" class="headerlink" title="Configuring Vim"></a>Configuring Vim</h2><p>Customizing Vim’s behavior can greatly enhance your editing experience. Here’s a technique to respond to events using autocommands:</p><ul><li>Using Autocommands to Respond to Events:<ul><li>Vim triggers the <code>BufReadPost</code> command (<code>:h BufReadPost</code>) after reading a file into a buffer. If the file path matches the pattern defined in our autocommand, Vim executes the specified <code>{command}</code>. Since we use a wildcard <code>*</code> in this example, the autocommand applies to all buffers.</li><li>Sometimes, there might be more suitable events. For such cases, you can achieve similar results by listening to the <code>FileType</code> event (<code>:h FileType</code>).<ul><li><code>autocmd BufWritePre /tmp/* setlocal noundofile</code></li></ul></li><li>The autocommand we defined is triggered by the <code>User</code> event with the pattern <code>ProjectionistActivate</code>. The <code>User</code> event doesn’t trigger automatically, but you can trigger such events yourself:<ul><li><code>:doautocmd User ProjectionistActivate</code></li></ul></li></ul></li></ul><p>In this article, we’ve explored various tips and techniques from “Modern Vim” to help you enhance your Vim editing skills. By incorporating these techniques into your workflow, you can become a more efficient and productive Vim user.</p>]]></content>
    
    
      
      
    <summary type="html">&lt;p&gt;Vim is a powerful and versatile text editor that offers a wide range of features and customization options. In this article, we will expl</summary>
      
    
    
    
    <category term="Productivity" scheme="https://ihugo.cc/categories/Productivity/"/>
    
    
    <category term="Vim" scheme="https://ihugo.cc/tags/Vim/"/>
    
  </entry>
  
  <entry>
    <title>入门（keystone文档翻译）</title>
    <link href="https://ihugo.cc/2018/10/30/000-keystone-getting-started/"/>
    <id>https://ihugo.cc/2018/10/30/000-keystone-getting-started/</id>
    <published>2018-10-30T20:43:52.000Z</published>
    <updated>2026-05-25T03:24:31.409Z</updated>
    
    <content type="html"><![CDATA[<h2 id="什么是Keystone？"><a href="#什么是Keystone？" class="headerlink" title="什么是Keystone？"></a>什么是Keystone？</h2><!--Keystone is a powerful Node.js content management system and web app framework built on the Express web framework and Mongoose ODM. Mongoose is an Object-Document Mapper (ODM) which provides a schema-based solution for modelling data and relationships for documents stored in the MongoDB database server. Keystone extends Mongoose's schema-based models with the concept of a Keystone List that helps you build beautiful Admin UIs with intelligent field types.--><p>Keystone是一个强大的Node.js内容管理系统，它是建立在Express和Mongoose ODM上的Web App框架。Mongoose ODM是面向文档映射，为那些存储到MongoDB数据库中文档，其提供了面向基于模式的解决方案来为数据和关系建模。Keystone扩展了Mongoose基于模式模型。Keystone列表利用智能字段类型帮助你构建漂亮的Admin界面。</p><!--Keystone aims to make it easy for you to create sophisticated web sites and applications, but without restricting how much you can customise your projects. You can bring your own view engine, structure your routes however you want, and modify your data structures to suit your requirements.--><p>Keystone的目标是让你更容易的构建复杂网站和应用，而不限制你自定义功能。你可以引入你自己的视图引擎，设计你想要的路由，并且修改你的数据结构来适应你的需求。</p><h2 id="先决条件"><a href="#先决条件" class="headerlink" title="先决条件"></a>先决条件</h2><!--Make sure you have the Node.js JavaScript runtime installed. Keystone 4 has been most throughly tested with Node 6, but should be compatible with newer production releases. We recommend using Node LTS (Long Term Support) releases for their extended support lifecycle and stability.--><p>确保你安装好了Node.js JavaScript运行环境。Keystone 4主要是在Node 6上进行测试，但是应当是和最新正式版Node是兼容的。我们推荐使用Node LTS（长期支持）版本，因为更长的支持周期和稳定性。（译者注：LTS 版本更注重稳定性和扩展支持，通常将支持 30 个月。）</p><!--You will also need to access to a MongoDB database server either installed locally or hosted remotely. Keystone 4 should be compatible with current production releases of MongoDB.--><p>你也要可以访问一个MongoDB数据库，不管是安装在本地还是部署在远程服务器上。Keystone 4兼容最新的MongoDB正式版。</p><!--You will need a reasonable working knowledge of JavaScript, Node.js, and npm to use Keystone. Basic familiarity with database concepts and MongoDB would also be helpful.--><p>你需要有一定的JavaScript、Node.js和npm的知识来使用Keystone。了解数据库概念和MongoDB则更有帮助。</p><h2 id="从哪里开始？"><a href="#从哪里开始？" class="headerlink" title="从哪里开始？"></a>从哪里开始？</h2><h3 id="快速开始指导"><a href="#快速开始指导" class="headerlink" title="快速开始指导"></a>快速开始指导</h3><!--If you want to get a project up and running locally with minimal fuss, we recommend starting with the Keystone Yeoman Generator. The Keystone Yeoman Generator provides a prompted experience to help you generate a full project ready to npm start with optional features like a basic blog, feedback form, and image gallery.--><p>如果你想要在本地快速运行一个项目，我们推荐你从Keystone Yeoman生成器开始。Keystone Yeoman生成器提供了一个提示命令行来帮助你生成完整的项目，可以通过npm来定制一些特性如基础的博客，反馈表单，相册功能。</p><h3 id="从脚手架开始设置"><a href="#从脚手架开始设置" class="headerlink" title="从脚手架开始设置"></a>从脚手架开始设置</h3><!--If you prefer to write your code from scratch, check out our four part Setting Up guide. This guide will walk you through setting up the core files and settings to get you started with Keystone.--><p>如果你更喜欢从脚手架编写你的代码，产看我们第四部分的设置教程。这个教程让你从核心的文件和设置来开始Keystone。</p><h2 id="接下来去哪？"><a href="#接下来去哪？" class="headerlink" title="接下来去哪？"></a>接下来去哪？</h2><!--Check out our documentation if you want to learn more about Keystone and the available options. Database Configuration is a good place to start, as are the Keystone Setup Options for general configuration.--><p>如果你想要学习更多的Keystone设置选项，请查阅我们的文档。数据库配置是一个好的起点，同样Keystone通用设置选项也是不错的选择。</p>]]></content>
    
    
      
      
    <summary type="html">&lt;h2 id=&quot;什么是Keystone？&quot;&gt;&lt;a href=&quot;#什么是Keystone？&quot; class=&quot;headerlink&quot; title=&quot;什么是Keystone？&quot;&gt;&lt;/a&gt;什么是Keystone？&lt;/h2&gt;&lt;!--Keystone is a powerful Node.j</summary>
      
    
    
    
    
    <category term="Life/Meta" scheme="https://ihugo.cc/tags/Life-Meta/"/>
    
    <category term="keystone" scheme="https://ihugo.cc/tags/keystone/"/>
    
  </entry>
  
</feed>
