<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.3.3">Jekyll</generator><link href="https://swiftbydeya.com/feed.xml" rel="self" type="application/atom+xml" /><link href="https://swiftbydeya.com/" rel="alternate" type="text/html" /><updated>2026-05-02T13:25:05+03:00</updated><id>https://swiftbydeya.com/feed.xml</id><title type="html">Swift By Deya</title><subtitle>Swift Insights: Articles on iOS Development</subtitle><entry><title type="html">LLDB Survival Guide: Theory, Tricks, and Python Hooks</title><link href="https://swiftbydeya.com/lldb-survival-guide/" rel="alternate" type="text/html" title="LLDB Survival Guide: Theory, Tricks, and Python Hooks" /><published>2026-04-04T00:00:00+03:00</published><updated>2026-04-04T00:00:00+03:00</updated><id>https://swiftbydeya.com/lldb-tricks</id><content type="html" xml:base="https://swiftbydeya.com/lldb-survival-guide/"><![CDATA[<p>LLDB is more than a place to set breakpoints. You can inspect app state, test quick fixes without rebuilding, and automate common debug steps. This guide keeps things practical and beginner-friendly.</p>

<!--more-->

<!-- _includes/centered-image.html -->
<div class="centered-image">
  <img src="../images/covers/lldb_full.webp" alt="LLDB console close up" class="centered-image__img" width="960" height="1568" />
  <p align="center">
    <em>LLDB in its natural habitat</em>
  </p>
</div>

<h2 id="start-here-what-lldb-is-and-how-to-run-it">Start Here: What LLDB Is and How to Run It</h2>

<p><code class="language-plaintext highlighter-rouge">LLDB</code> (sometimes typed as <code class="language-plaintext highlighter-rouge">lldb</code> in lowercase by mistake) is the debugger that ships with Xcode command line tools.</p>

<p>If your app is not running yet, launch it under LLDB:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>xcrun lldb /path/to/MyApp
</code></pre></div></div>

<p>Then in the LLDB prompt:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">(</span>lldb<span class="o">)</span> run
</code></pre></div></div>

<p>If your app is already running, attach to it:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>pgrep <span class="nt">-x</span> MyApp
xcrun lldb <span class="nt">-p</span> &lt;pid&gt;
</code></pre></div></div>

<p>Or from inside LLDB:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">(</span>lldb<span class="o">)</span> attach <span class="nt">--name</span> MyApp
</code></pre></div></div>

<p>To exit:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">(</span>lldb<span class="o">)</span> quit
</code></pre></div></div>

<h2 id="why-attaching-to-production-apps-can-fail">Why Attaching To Production Apps Can Fail</h2>

<p>Many people hit this early: attaching to production macOS apps is often blocked by security rules.</p>

<p>Main reasons:</p>

<ul>
  <li>SIP (System Integrity Protection) blocks debugging many protected/system processes.</li>
  <li>Hardened Runtime + missing <code class="language-plaintext highlighter-rouge">get-task-allow</code> entitlement can block debugger attach.</li>
  <li>App Store/release builds are usually signed to prevent casual attaching.</li>
</ul>

<p>So yes, sometimes attach starts working only after SIP is disabled on a test machine. But SIP is not the only gate. Even with SIP disabled, code-signing and entitlements can still block attach.</p>

<p>Check SIP status:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>csrutil status
</code></pre></div></div>

<p>If you are doing local security research on your own Mac only, disable SIP temporarily:</p>

<ol>
  <li>Reboot into Recovery.</li>
  <li>Open Terminal in Recovery.</li>
  <li>Run <code class="language-plaintext highlighter-rouge">csrutil disable</code>.</li>
  <li>Reboot normally.</li>
</ol>

<p>Warning: this is dangerous. Disabling SIP removes important macOS protections that block system tampering and many privilege-escalation paths. If malware runs while SIP is off, it has a much easier time persisting and modifying protected files/processes. Do this only on an isolated test machine, never on your daily-use Mac, and re-enable SIP immediately after testing.</p>

<p>Re-enable it when done:</p>

<ol>
  <li>Reboot into Recovery again.</li>
  <li>Run <code class="language-plaintext highlighter-rouge">csrutil enable</code>.</li>
  <li>Reboot normally.</li>
</ol>

<h2 id="a-fast-theory-primer">A Fast Theory Primer</h2>

<p>LLDB has two parts:</p>

<ul>
  <li>The command line you type into.</li>
  <li>An internal toolkit called the <code class="language-plaintext highlighter-rouge">SB API</code> (<code class="language-plaintext highlighter-rouge">SB</code> = <strong>Script Bridge</strong> in LLDB docs).</li>
</ul>

<p>Note: this is LLDB’s API naming and is different from the separate macOS <code class="language-plaintext highlighter-rouge">ScriptingBridge</code> framework.</p>

<p>What is the <code class="language-plaintext highlighter-rouge">SB API object model</code>?</p>

<p>It is just LLDB’s internal set of objects (like building blocks): app, process, thread, stack frame, variables, and symbols. LLDB commands use these objects behind the scenes.</p>

<p>Typical flow:</p>

<ul>
  <li>Create or select the app to debug (called a “target”): <code class="language-plaintext highlighter-rouge">target create ...</code></li>
  <li>Launch or attach: <code class="language-plaintext highlighter-rouge">run</code> / <code class="language-plaintext highlighter-rouge">attach ...</code></li>
  <li>Check liveness: <code class="language-plaintext highlighter-rouge">process status</code></li>
  <li>Inspect call stack and local values: <code class="language-plaintext highlighter-rouge">frame info</code>, <code class="language-plaintext highlighter-rouge">frame variable -L</code> (or alias <code class="language-plaintext highlighter-rouge">v -L</code>)</li>
</ul>

<p><code class="language-plaintext highlighter-rouge">expr -- &lt;code&gt;</code> runs code inside the paused app. Start with read-only checks. Only change values on purpose.</p>

<h2 id="quick-commands-i-actually-use">Quick Commands I Actually Use</h2>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(lldb) breakpoint set --name viewDidLoad
(lldb) thread step-in
(lldb) thread backtrace
(lldb) frame variable request
(lldb) expr -l objc -O -- [[[UIApplication sharedApplication] connectedScenes] allObjects]
(lldb) memory read --format x --size 8 --count 8 $sp
</code></pre></div></div>

<p>Speed tips: <code class="language-plaintext highlighter-rouge">br s -n</code>, <code class="language-plaintext highlighter-rouge">bt</code>, <code class="language-plaintext highlighter-rouge">v</code> (or <code class="language-plaintext highlighter-rouge">frame var</code>), and <code class="language-plaintext highlighter-rouge">mem r</code> are short versions of common commands. Use <code class="language-plaintext highlighter-rouge">--one-shot true</code> for temporary breakpoints so they remove themselves after one hit.</p>

<h2 id="anatomy-of-a-stop">Anatomy of a Stop</h2>

<p>LLDB can stop because of a breakpoint, crash signal, exception, or watchpoint. First step: find out why it stopped.</p>

<p>Common crash signals/exceptions you will see:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">EXC_BAD_ACCESS</code> / <code class="language-plaintext highlighter-rouge">SIGSEGV</code>: invalid memory access (use-after-free, null/garbage pointer).</li>
  <li><code class="language-plaintext highlighter-rouge">SIGABRT</code>: app called <code class="language-plaintext highlighter-rouge">abort()</code> (failed assertion, fatal error, uncaught runtime issue).</li>
  <li><code class="language-plaintext highlighter-rouge">SIGILL</code>: illegal CPU instruction (corrupted state, bad jump, unsupported instruction path).</li>
</ul>

<p><code class="language-plaintext highlighter-rouge">process status</code> tells you if you hit <code class="language-plaintext highlighter-rouge">EXC_BAD_ACCESS</code>, <code class="language-plaintext highlighter-rouge">SIGSEGV</code>, or a normal breakpoint. <code class="language-plaintext highlighter-rouge">thread backtrace all</code> shows what every thread is doing, which helps with hangs. <code class="language-plaintext highlighter-rouge">thread return</code> can skip the current function, but use it carefully because it changes app behavior.</p>

<p>Breakpoint vs watchpoint (quick difference):</p>

<ul>
  <li>Breakpoint: stops when execution reaches a code location (file/line/function).</li>
  <li>Watchpoint: stops when a specific value in memory is read/written/modified.</li>
</ul>

<p>If you get expected signals like <code class="language-plaintext highlighter-rouge">SIGPIPE</code>, tune handling explicitly:</p>

<p><code class="language-plaintext highlighter-rouge">process handle SIGPIPE -n true -p true -s false</code></p>

<h2 id="breakpoints-that-pull-their-weight">Breakpoints That Pull Their Weight</h2>

<p><code class="language-plaintext highlighter-rouge">br s -f File.swift -l 42</code> sets a breakpoint at one exact line. <code class="language-plaintext highlighter-rouge">br s -r "viewDidLoad"</code> matches many functions by name pattern. Conditional breakpoints like <code class="language-plaintext highlighter-rouge">br s -n foo -c 'count &gt; 10'</code> stop only when a rule is true.</p>

<p>Auto-commands are great for data capture:</p>

<ul>
  <li>Set breakpoint: <code class="language-plaintext highlighter-rouge">br s -n willDisplayCell</code></li>
  <li>Add actions: <code class="language-plaintext highlighter-rouge">br command add &lt;id&gt;</code></li>
  <li>Add commands like <code class="language-plaintext highlighter-rouge">thread backtrace</code>, <code class="language-plaintext highlighter-rouge">frame variable model</code>, then <code class="language-plaintext highlighter-rouge">continue</code></li>
</ul>

<p>Watchpoints stop when a value changes:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">watchpoint set variable myVar</code></li>
  <li><code class="language-plaintext highlighter-rouge">watchpoint set variable -w read_write myVar</code></li>
  <li><code class="language-plaintext highlighter-rouge">watchpoint set expression -w modify -- &amp;myVar</code></li>
</ul>

<h3 id="tracepoints-without-pausing">Tracepoints Without Pausing</h3>

<p>When you want logs but do not want to pause:</p>

<p><code class="language-plaintext highlighter-rouge">br s -n foo -C 'expr -O -- foo' -G true</code></p>

<p>Add <code class="language-plaintext highlighter-rouge">--ignore-count</code> to skip the first N hits.</p>

<h2 id="reading-swift-nicely">Reading Swift Nicely</h2>

<p>Swift values can look cleaner with these settings:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">settings set target.swift-demangle true</code></li>
  <li><code class="language-plaintext highlighter-rouge">settings set target.max-children-count 256</code></li>
  <li><code class="language-plaintext highlighter-rouge">settings set target.process.optimization-warnings true</code></li>
</ul>

<p>Use <code class="language-plaintext highlighter-rouge">expr -l swift -O --</code> for Swift objects (equivalent to <code class="language-plaintext highlighter-rouge">po</code> with explicit Swift context) and <code class="language-plaintext highlighter-rouge">expr -l objc -O --</code> for Objective-C objects. In Release/optimized builds, some local variables may be unavailable; this is normal.</p>

<h2 id="productionoptimized-build-survival">Production/Optimized Build Survival</h2>

<p><code class="language-plaintext highlighter-rouge">settings set symbols.enable-external-lookup true</code> can help LLDB find extra symbol info. If local values are optimized away, check raw CPU registers (<code class="language-plaintext highlighter-rouge">register read</code>) and memory (<code class="language-plaintext highlighter-rouge">memory read</code>) near <code class="language-plaintext highlighter-rouge">$sp</code>/<code class="language-plaintext highlighter-rouge">$fp</code>.</p>

<p>Noise control while stepping:</p>

<p><code class="language-plaintext highlighter-rouge">settings append target.process.thread.step-avoid-libraries UIKitCore</code></p>

<p><code class="language-plaintext highlighter-rouge">thread jump --by 1</code> can skip one source line. Use as a last resort.</p>

<p>If tracing is supported in your environment, this sequence is useful:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">thread trace start</code></li>
  <li><code class="language-plaintext highlighter-rouge">thread trace dump instructions</code></li>
</ul>

<h2 id="memory-work-from-sanity-checks-to-surgery">Memory Work: From Sanity Checks to Surgery</h2>

<p>Stack sanity check:</p>

<p><code class="language-plaintext highlighter-rouge">memory read --format x --size 8 --count 4 $fp</code></p>

<p>Address-to-symbol mapping:</p>

<p><code class="language-plaintext highlighter-rouge">image lookup -a 0xADDR</code></p>

<p>Controlled mutation during experiments:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">expr -- myValue = 0</code></li>
  <li><code class="language-plaintext highlighter-rouge">memory write &lt;addr&gt; &lt;bytes&gt;</code></li>
</ul>

<p>For memory allocation history, try <code class="language-plaintext highlighter-rouge">memory history &lt;address&gt;</code> when available. For memory-region details, use <code class="language-plaintext highlighter-rouge">memory region &lt;address&gt;</code> and <code class="language-plaintext highlighter-rouge">vmmap</code> (a macOS terminal tool).</p>

<h2 id="asyncawait-actors-and-queues">Async/Await, Actors, and Queues</h2>

<p><code class="language-plaintext highlighter-rouge">thread list</code> gives a quick view of active threads and queue names. <code class="language-plaintext highlighter-rouge">thread info -s</code> gives extra stop details.</p>

<p>Useful concurrency breakpoints:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">br s -n _swift_task_switch</code></li>
  <li><code class="language-plaintext highlighter-rouge">br s -n swift_task_enqueue</code></li>
</ul>

<p>To find where a task was launched, break in the task body and inspect <code class="language-plaintext highlighter-rouge">bt</code> for concurrency runtime frames around your app frames.</p>

<h2 id="crash-and-hang-triage-playbook">Crash and Hang Triage Playbook</h2>

<p>For hangs:</p>

<ol>
  <li><code class="language-plaintext highlighter-rouge">process interrupt</code></li>
  <li><code class="language-plaintext highlighter-rouge">thread backtrace all</code></li>
  <li>Check for waits like <code class="language-plaintext highlighter-rouge">pthread_mutex_lock</code> and <code class="language-plaintext highlighter-rouge">dispatch_semaphore_wait</code></li>
</ol>

<p>For crashes, start with the crashing thread: run <code class="language-plaintext highlighter-rouge">process status</code>, then <code class="language-plaintext highlighter-rouge">frame info</code> on that thread. If shared state looks suspicious, add watchpoints and rerun.</p>

<p>To keep a point-in-time artifact for offline analysis:</p>

<p><code class="language-plaintext highlighter-rouge">process save-core /tmp/foo.core</code></p>

<h2 id="remote-and-core-file-debugging">Remote and Core-File Debugging</h2>

<p>Remote iOS debugging usually starts with:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">platform select remote-ios</code></li>
  <li><code class="language-plaintext highlighter-rouge">platform connect connect://&lt;host&gt;:&lt;port&gt;</code></li>
</ul>

<p>Then add symbols locally (<code class="language-plaintext highlighter-rouge">target symbols add ...</code>) and verify loaded images with <code class="language-plaintext highlighter-rouge">image list</code>.</p>

<p>For core files:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">target create --core crash.core</code></li>
  <li><code class="language-plaintext highlighter-rouge">thread backtrace all</code></li>
  <li><code class="language-plaintext highlighter-rouge">thread select &lt;index&gt;</code> to inspect specific crashed paths</li>
</ul>

<h2 id="symbols-and-dsyms">Symbols and dSYMs</h2>

<p>Keep <code class="language-plaintext highlighter-rouge">.dSYM</code> files available. They help LLDB map memory addresses back to file names and line numbers. Configure search paths in <code class="language-plaintext highlighter-rouge">.lldbinit</code> (<code class="language-plaintext highlighter-rouge">target.exec-search-paths</code>, <code class="language-plaintext highlighter-rouge">target.debug-file-search-paths</code>).</p>

<p>Verification loop:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">image list -b</code> for UUID/slide info</li>
  <li><code class="language-plaintext highlighter-rouge">dwarfdump --uuid</code> to confirm matches</li>
  <li>fallback symbolication with <code class="language-plaintext highlighter-rouge">atos -o MyApp -arch arm64 -l &lt;slide&gt; &lt;addr&gt;</code> if needed</li>
</ul>

<h2 id="python-hooking-your-own-commands">Python: Hooking Your Own Commands</h2>

<p>LLDB supports Python. You can add your own custom commands. If you are new, you can skip this section and come back later.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># save as ~/lldb_tools/retain_cycles.py
</span><span class="kn">import</span> <span class="n">lldb</span>

<span class="k">def</span> <span class="nf">find_retain_cycles</span><span class="p">(</span><span class="n">debugger</span><span class="p">,</span> <span class="n">command</span><span class="p">,</span> <span class="n">exe_ctx</span><span class="p">,</span> <span class="n">result</span><span class="p">,</span> <span class="n">_</span><span class="p">):</span>
    <span class="sh">"""</span><span class="s">Demo command: run a Swift helper method on an expression.</span><span class="sh">"""</span>
    <span class="n">cmd</span> <span class="o">=</span> <span class="sa">f</span><span class="sh">"</span><span class="s">expr -l swift -O -- </span><span class="si">{</span><span class="n">command</span><span class="si">}</span><span class="s">.debugRetainCycles()</span><span class="sh">"</span>
    <span class="n">res</span> <span class="o">=</span> <span class="n">lldb</span><span class="p">.</span><span class="nc">SBCommandReturnObject</span><span class="p">()</span>
    <span class="n">debugger</span><span class="p">.</span><span class="nc">GetCommandInterpreter</span><span class="p">().</span><span class="nc">HandleCommand</span><span class="p">(</span><span class="n">cmd</span><span class="p">,</span> <span class="n">res</span><span class="p">)</span>

    <span class="k">if</span> <span class="n">res</span><span class="p">.</span><span class="nc">Succeeded</span><span class="p">():</span>
        <span class="n">result</span><span class="p">.</span><span class="nc">AppendMessage</span><span class="p">(</span><span class="n">res</span><span class="p">.</span><span class="nc">GetOutput</span><span class="p">()</span> <span class="ow">or</span> <span class="sh">""</span><span class="p">)</span>
    <span class="k">else</span><span class="p">:</span>
        <span class="n">result</span><span class="p">.</span><span class="nc">SetError</span><span class="p">(</span><span class="n">res</span><span class="p">.</span><span class="nc">GetError</span><span class="p">()</span> <span class="ow">or</span> <span class="sh">"</span><span class="s">Expression failed</span><span class="sh">"</span><span class="p">)</span>

<span class="k">def</span> <span class="nf">__lldb_init_module</span><span class="p">(</span><span class="n">debugger</span><span class="p">,</span> <span class="n">_internal_dict</span><span class="p">):</span>
    <span class="n">debugger</span><span class="p">.</span><span class="nc">HandleCommand</span><span class="p">(</span>
        <span class="sh">"</span><span class="s">command script add -f retain_cycles.find_retain_cycles rcfind</span><span class="sh">"</span>
    <span class="p">)</span>
    <span class="nf">print</span><span class="p">(</span><span class="sh">"</span><span class="s">Registered: rcfind &lt;expression&gt;</span><span class="sh">"</span><span class="p">)</span>
</code></pre></div></div>

<p>Wire it in <code class="language-plaintext highlighter-rouge">.lldbinit</code>:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>command script import ~/lldb_tools/retain_cycles.py
</code></pre></div></div>

<p>Use it in-session:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>(lldb) rcfind myController
</code></pre></div></div>

<h3 id="a-more-involved-hook-auto-dump-on-crash-stops">A More Involved Hook: Auto-Dump on Crash Stops</h3>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># ~/lldb_tools/on_crash_dump.py
</span><span class="kn">import</span> <span class="n">datetime</span>
<span class="kn">import</span> <span class="n">lldb</span>

<span class="n">CRASH_REASONS</span> <span class="o">=</span> <span class="p">{</span>
    <span class="n">lldb</span><span class="p">.</span><span class="n">eStopReasonException</span><span class="p">,</span>
    <span class="n">lldb</span><span class="p">.</span><span class="n">eStopReasonSignal</span><span class="p">,</span>
<span class="p">}</span>

<span class="k">class</span> <span class="nc">CrashDumpHook</span><span class="p">:</span>
    <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="n">self</span><span class="p">,</span> <span class="n">target</span><span class="p">,</span> <span class="n">_extra_args</span><span class="p">,</span> <span class="n">_internal_dict</span><span class="p">):</span>
        <span class="n">self</span><span class="p">.</span><span class="n">target</span> <span class="o">=</span> <span class="n">target</span>

    <span class="k">def</span> <span class="nf">handle_stop</span><span class="p">(</span><span class="n">self</span><span class="p">,</span> <span class="n">exe_ctx</span><span class="p">,</span> <span class="n">stream</span><span class="p">):</span>
        <span class="n">thread</span> <span class="o">=</span> <span class="n">exe_ctx</span><span class="p">.</span><span class="nc">GetThread</span><span class="p">()</span>
        <span class="k">if</span> <span class="ow">not</span> <span class="n">thread</span><span class="p">.</span><span class="nc">IsValid</span><span class="p">():</span>
            <span class="k">return</span> <span class="bp">False</span>

        <span class="n">reason</span> <span class="o">=</span> <span class="n">thread</span><span class="p">.</span><span class="nc">GetStopReason</span><span class="p">()</span>
        <span class="k">if</span> <span class="n">reason</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">CRASH_REASONS</span><span class="p">:</span>
            <span class="k">return</span> <span class="bp">False</span>

        <span class="n">ts</span> <span class="o">=</span> <span class="n">datetime</span><span class="p">.</span><span class="n">datetime</span><span class="p">.</span><span class="nf">now</span><span class="p">().</span><span class="nf">strftime</span><span class="p">(</span><span class="sh">"</span><span class="s">%Y%m%d-%H%M%S</span><span class="sh">"</span><span class="p">)</span>
        <span class="n">path</span> <span class="o">=</span> <span class="sa">f</span><span class="sh">"</span><span class="s">/tmp/lldb-crash-</span><span class="si">{</span><span class="n">ts</span><span class="si">}</span><span class="s">.txt</span><span class="sh">"</span>

        <span class="n">res</span> <span class="o">=</span> <span class="n">lldb</span><span class="p">.</span><span class="nc">SBCommandReturnObject</span><span class="p">()</span>
        <span class="n">interp</span> <span class="o">=</span> <span class="n">self</span><span class="p">.</span><span class="n">target</span><span class="p">.</span><span class="nc">GetDebugger</span><span class="p">().</span><span class="nc">GetCommandInterpreter</span><span class="p">()</span>
        <span class="n">interp</span><span class="p">.</span><span class="nc">HandleCommand</span><span class="p">(</span><span class="sh">"</span><span class="s">thread backtrace all</span><span class="sh">"</span><span class="p">,</span> <span class="n">res</span><span class="p">)</span>

        <span class="k">with</span> <span class="nf">open</span><span class="p">(</span><span class="n">path</span><span class="p">,</span> <span class="sh">"</span><span class="s">w</span><span class="sh">"</span><span class="p">,</span> <span class="n">encoding</span><span class="o">=</span><span class="sh">"</span><span class="s">utf-8</span><span class="sh">"</span><span class="p">)</span> <span class="k">as</span> <span class="n">f</span><span class="p">:</span>
            <span class="n">f</span><span class="p">.</span><span class="nf">write</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">Stop reason enum: </span><span class="si">{</span><span class="n">reason</span><span class="si">}</span><span class="se">\n\n</span><span class="sh">"</span><span class="p">)</span>
            <span class="n">f</span><span class="p">.</span><span class="nf">write</span><span class="p">(</span><span class="n">res</span><span class="p">.</span><span class="nc">GetOutput</span><span class="p">()</span> <span class="ow">or</span> <span class="sh">""</span><span class="p">)</span>

        <span class="n">stream</span><span class="p">.</span><span class="nc">Printf</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">Wrote crash dump to </span><span class="si">{</span><span class="n">path</span><span class="si">}</span><span class="se">\n</span><span class="sh">"</span><span class="p">)</span>
        <span class="k">return</span> <span class="bp">False</span>

<span class="k">def</span> <span class="nf">__lldb_init_module</span><span class="p">(</span><span class="n">debugger</span><span class="p">,</span> <span class="n">_dict</span><span class="p">):</span>
    <span class="n">debugger</span><span class="p">.</span><span class="nc">HandleCommand</span><span class="p">(</span>
        <span class="sh">"</span><span class="s">target stop-hook add -P on_crash_dump.CrashDumpHook</span><span class="sh">"</span>
    <span class="p">)</span>
    <span class="nf">print</span><span class="p">(</span><span class="sh">"</span><span class="s">Registered crash stop hook</span><span class="sh">"</span><span class="p">)</span>
</code></pre></div></div>

<p>Add to <code class="language-plaintext highlighter-rouge">.lldbinit</code>:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>command script import ~/lldb_tools/on_crash_dump.py
</code></pre></div></div>

<p>This saves crash dumps only on real crash-like stops, not on every breakpoint.</p>

<h2 id="favorite-lldbinit-snippets">Favorite <code class="language-plaintext highlighter-rouge">.lldbinit</code> Snippets</h2>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>settings set stop-disassembly-count 4
settings set target.process.thread.step-out-avoids-no-debug true
settings set target.process.thread.step-in-avoids-no-debug true
settings set target.max-children-count 256
settings set target.swift-demangle true
command alias bt thread backtrace
command alias frv frame variable -L
command alias memr memory read --format x --size 8 --count 8
</code></pre></div></div>

<h2 id="automating-sessions">Automating Sessions</h2>

<p>Small automations that save time:</p>

<ul>
  <li>Keep recurring breakpoints in a file and load with <code class="language-plaintext highlighter-rouge">command source my_breakpoints.lldb</code></li>
  <li>Add quick diagnostics: <code class="language-plaintext highlighter-rouge">target stop-hook add -o "thread backtrace"</code></li>
  <li>Turn on protocol logging when needed: <code class="language-plaintext highlighter-rouge">log enable gdb-remote packets</code></li>
</ul>

<h2 id="performance-poking-without-instruments">Performance Poking Without Instruments</h2>

<p>For instruction-level hotspots:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">thread step-inst</code></li>
  <li><code class="language-plaintext highlighter-rouge">thread until -a &lt;addr&gt;</code></li>
</ul>

<p>For symbol-focused checks:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">image lookup -n objc_msgSend</code></li>
  <li><code class="language-plaintext highlighter-rouge">br s -a &lt;addr&gt;</code> with a suitable condition</li>
</ul>

<p><code class="language-plaintext highlighter-rouge">statistics dump</code> helps you see if LLDB itself is slow (for example, symbol loading or heavy expression use).</p>

<h2 id="ui-debugging-without-xcode">UI Debugging Without Xcode</h2>

<p>Rendering and layout:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">br s -n drawRect:</code></li>
  <li><code class="language-plaintext highlighter-rouge">br s -n layoutSubviews</code></li>
</ul>

<p>Responder-chain probing:</p>

<p><code class="language-plaintext highlighter-rouge">expr -l objc -O -- [UIResponder targetForAction:@selector(description) withSender:nil]</code></p>

<p>Text view hierarchy dump (old but still useful in UIKit debugging):</p>

<p><code class="language-plaintext highlighter-rouge">expr -l objc -O -- [[[UIApplication sharedApplication] keyWindow] recursiveDescription]</code></p>

<h2 id="when-lldb-misbehaves">When LLDB Misbehaves</h2>

<p>If LLDB starts slowly or acts weird, first simplify your <code class="language-plaintext highlighter-rouge">.lldbinit</code>. If module caches are broken, clear <code class="language-plaintext highlighter-rouge">~/Library/Developer/Xcode/DerivedData/ModuleCache*</code>.</p>

<p>If expressions fail unexpectedly, force the language:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">expr -l objc -- ...</code></li>
  <li><code class="language-plaintext highlighter-rouge">expr -l swift -- ...</code></li>
</ul>

<p>If remote attach fails repeatedly, restart the target device and clean up stale <code class="language-plaintext highlighter-rouge">debugserver</code> processes on host and device.</p>

<h2 id="tiny-cheat-sheet-copypaste">Tiny Cheat Sheet (copy/paste)</h2>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>br s -n method
br s -f File.swift -l 88
br s -n foo -c 'x &gt; 3'
br s -n foo --one-shot true
watchpoint set variable -w read_write myVar
thread backtrace all
frame variable -L
expr -l swift -O -- myObj.debugDescription()
memory read --format x --size 8 --count 4 $sp
process handle SIGPIPE -n true -p true -s false
</code></pre></div></div>

<p>LLDB gets easier with repetition. Keep a small command list you trust, and add automation later.</p>]]></content><author><name>Deya Elkhawaldeh</name></author><category term="Development" /><category term="Debugging" /><category term="Programming" /><category term="Swift" /><category term="LLDB" /><category term="Debugging" /><category term="Swift" /><category term="macOS" /><summary type="html"><![CDATA[LLDB is more than a place to set breakpoints. You can inspect app state, test quick fixes without rebuilding, and automate common debug steps. This guide keeps things practical and beginner-friendly.]]></summary></entry><entry><title type="html">Phantom Types in Swift</title><link href="https://swiftbydeya.com/swift-phantom-types/" rel="alternate" type="text/html" title="Phantom Types in Swift" /><published>2025-10-15T00:00:00+03:00</published><updated>2025-10-15T00:00:00+03:00</updated><id>https://swiftbydeya.com/swift-phantom-types</id><content type="html" xml:base="https://swiftbydeya.com/swift-phantom-types/"><![CDATA[<p>Phantom types let you tuck extra meaning into Swift’s type system without changing anything at runtime. They live in a generic parameter purely for compile-time checks, which means you can stop category mistakes (mixing meters with feet or safe SQL with raw text) while keeping zero overhead. If you are new to Swift’s type system, think of phantom types as labels the compiler reads, not values your app carries around.</p>

<!--more-->

<!-- _includes/centered-image.html -->
<div class="centered-image">
  <img src="../images/covers/swift_phantom_types_full.webp" alt="Phantom Types In Swift" class="centered-image__img" width="960" height="1568" />
  <p align="center">
    <em>Phantom Types In Swift</em>
  </p>
</div>

<h2 id="the-basic-idea">The basic idea</h2>

<p>Consider a <code class="language-plaintext highlighter-rouge">Tagged</code> wrapper that carries a <code class="language-plaintext highlighter-rouge">Raw</code> value and a phantom <code class="language-plaintext highlighter-rouge">Tag</code> type:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">struct</span> <span class="kt">Tagged</span><span class="o">&lt;</span><span class="kt">Tag</span><span class="p">,</span> <span class="kt">Raw</span><span class="o">&gt;</span> <span class="p">{</span>
    <span class="k">let</span> <span class="nv">raw</span><span class="p">:</span> <span class="kt">Raw</span>
    <span class="nf">init</span><span class="p">(</span><span class="n">_</span> <span class="nv">raw</span><span class="p">:</span> <span class="kt">Raw</span><span class="p">)</span> <span class="p">{</span> <span class="k">self</span><span class="o">.</span><span class="n">raw</span> <span class="o">=</span> <span class="n">raw</span> <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">Tag</code> never appears as a stored property, but the compiler still tracks it. This means two wrappers with different tags are incompatible even if they share the same raw type.</p>

<h2 id="eliminating-category-errors">Eliminating category errors</h2>

<p>Imagine working with units. Create tiny, empty types to tag your values:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">enum</span> <span class="kt">MetersTag</span> <span class="p">{}</span>
<span class="kd">enum</span> <span class="kt">FeetTag</span> <span class="p">{}</span>

<span class="kd">typealias</span> <span class="kt">Meters</span> <span class="o">=</span> <span class="kt">Tagged</span><span class="o">&lt;</span><span class="kt">MetersTag</span><span class="p">,</span> <span class="kt">Double</span><span class="o">&gt;</span>
<span class="kd">typealias</span> <span class="kt">Feet</span>   <span class="o">=</span> <span class="kt">Tagged</span><span class="o">&lt;</span><span class="kt">FeetTag</span><span class="p">,</span> <span class="kt">Double</span><span class="o">&gt;</span>
</code></pre></div></div>

<p>Now a function like <code class="language-plaintext highlighter-rouge">distanceInMeters</code> only accepts <code class="language-plaintext highlighter-rouge">Meters</code>. Passing a <code class="language-plaintext highlighter-rouge">Feet</code> value will not compile, even though both carry a <code class="language-plaintext highlighter-rouge">Double</code> underneath. The compiler blocks the mix-up before it ever runs.</p>

<h2 id="safer-apis-with-compile-time-intent">Safer APIs with compile-time intent</h2>

<p>Say you’re building SQL. You can mark raw SQL differently from sanitized SQL:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">enum</span> <span class="kt">RawSQL</span> <span class="p">{}</span>
<span class="kd">enum</span> <span class="kt">SafeSQL</span> <span class="p">{}</span>

<span class="kd">typealias</span> <span class="kt">SQL</span><span class="o">&lt;</span><span class="kt">Flavor</span><span class="o">&gt;</span> <span class="o">=</span> <span class="kt">Tagged</span><span class="o">&lt;</span><span class="kt">Flavor</span><span class="p">,</span> <span class="kt">String</span><span class="o">&gt;</span>

<span class="kd">func</span> <span class="nf">sanitize</span><span class="p">(</span><span class="n">_</span> <span class="nv">input</span><span class="p">:</span> <span class="kt">SQL</span><span class="o">&lt;</span><span class="kt">RawSQL</span><span class="o">&gt;</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">SQL</span><span class="o">&lt;</span><span class="kt">SafeSQL</span><span class="o">&gt;</span> <span class="p">{</span>
    <span class="kt">SQL</span><span class="o">&lt;</span><span class="kt">SafeSQL</span><span class="o">&gt;</span><span class="p">(</span><span class="s">"sanitize(</span><span class="se">\(</span><span class="n">input</span><span class="o">.</span><span class="n">raw</span><span class="se">)</span><span class="s">)"</span><span class="p">)</span>
<span class="p">}</span>

<span class="kd">func</span> <span class="nf">execute</span><span class="p">(</span><span class="n">_</span> <span class="nv">query</span><span class="p">:</span> <span class="kt">SQL</span><span class="o">&lt;</span><span class="kt">SafeSQL</span><span class="o">&gt;</span><span class="p">)</span> <span class="p">{</span> <span class="cm">/* ... */</span> <span class="p">}</span>

<span class="k">let</span> <span class="nv">userInput</span> <span class="o">=</span> <span class="kt">SQL</span><span class="o">&lt;</span><span class="kt">RawSQL</span><span class="o">&gt;</span><span class="p">(</span><span class="s">"DROP TABLE users;"</span><span class="p">)</span>
<span class="k">let</span> <span class="nv">safe</span> <span class="o">=</span> <span class="nf">sanitize</span><span class="p">(</span><span class="n">userInput</span><span class="p">)</span>
<span class="nf">execute</span><span class="p">(</span><span class="n">safe</span><span class="p">)</span>           <span class="c1">// ✅</span>
<span class="c1">// execute(userInput)   // won’t compile, raw SQL rejected</span>
</code></pre></div></div>

<p>The same trick keeps coordinate systems honest:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">enum</span> <span class="kt">ViewSpace</span> <span class="p">{}</span>
<span class="kd">enum</span> <span class="kt">WorldSpace</span> <span class="p">{}</span>

<span class="kd">struct</span> <span class="kt">Point</span><span class="o">&lt;</span><span class="kt">Tag</span><span class="o">&gt;</span> <span class="p">{</span>
    <span class="k">var</span> <span class="nv">x</span><span class="p">:</span> <span class="kt">Double</span>
    <span class="k">var</span> <span class="nv">y</span><span class="p">:</span> <span class="kt">Double</span>
<span class="p">}</span>

<span class="kd">typealias</span> <span class="kt">ViewPoint</span>  <span class="o">=</span> <span class="kt">Point</span><span class="o">&lt;</span><span class="kt">ViewSpace</span><span class="o">&gt;</span>
<span class="kd">typealias</span> <span class="kt">WorldPoint</span> <span class="o">=</span> <span class="kt">Point</span><span class="o">&lt;</span><span class="kt">WorldSpace</span><span class="o">&gt;</span>

<span class="kd">func</span> <span class="nf">convertToWorld</span><span class="p">(</span><span class="n">_</span> <span class="nv">p</span><span class="p">:</span> <span class="kt">ViewPoint</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">WorldPoint</span> <span class="p">{</span>
    <span class="kt">WorldPoint</span><span class="p">(</span><span class="nv">x</span><span class="p">:</span> <span class="n">p</span><span class="o">.</span><span class="n">x</span> <span class="o">*</span> <span class="mi">2</span><span class="p">,</span> <span class="nv">y</span><span class="p">:</span> <span class="n">p</span><span class="o">.</span><span class="n">y</span> <span class="o">*</span> <span class="mi">2</span><span class="p">)</span>
<span class="p">}</span>

<span class="k">let</span> <span class="nv">local</span> <span class="o">=</span> <span class="kt">ViewPoint</span><span class="p">(</span><span class="nv">x</span><span class="p">:</span> <span class="mi">10</span><span class="p">,</span> <span class="nv">y</span><span class="p">:</span> <span class="mi">20</span><span class="p">)</span>
<span class="k">let</span> <span class="nv">world</span> <span class="o">=</span> <span class="nf">convertToWorld</span><span class="p">(</span><span class="n">local</span><span class="p">)</span>
<span class="c1">// convertToWorld(world) // won’t compile, can’t double-convert</span>
</code></pre></div></div>

<p>By tagging points, you cannot accidentally convert the same point twice or pass a world-space point where a view-space point belongs.</p>

<h2 id="real-world-swift-uses">Real-world Swift uses</h2>

<p>You can wrap identifiers to stop cross-wiring them. Tag <code class="language-plaintext highlighter-rouge">UserID</code> and <code class="language-plaintext highlighter-rouge">OrderID</code> so an order summary can never swap them. You can tag network payloads by version and make your decoder refuse to mix v1 with v2. You can tag UI points by layout or screen space to keep math from drifting between coordinate systems. If you are learning, start with a single place that often breaks (for example, mixed units) and add a tag there first. The pattern stays the same: a phantom tag carries meaning the compiler understands, but your runtime values remain lean.</p>

<p>You can take it further for navigation and routing. A typed router can insist on a <code class="language-plaintext highlighter-rouge">Route&lt;Authenticated&gt;</code> when users are logged in and a <code class="language-plaintext highlighter-rouge">Route&lt;Guest&gt;</code> otherwise. A naive string-based router cannot help you here, but a phantom tag can. The same applies to feature flags: wrap the flag in <code class="language-plaintext highlighter-rouge">Enabled</code> or <code class="language-plaintext highlighter-rouge">Disabled</code> tags so you do not accidentally ship the wrong code path. Beginners can think of this as adding an extra “safety stamp” that the compiler checks.</p>

<p>State machines benefit, too. Imagine modeling an onboarding flow:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">enum</span> <span class="kt">Welcome</span> <span class="p">{}</span>
<span class="kd">enum</span> <span class="kt">Permissions</span> <span class="p">{}</span>
<span class="kd">enum</span> <span class="kt">Completed</span> <span class="p">{}</span>

<span class="kd">struct</span> <span class="kt">OnboardingState</span><span class="o">&lt;</span><span class="kt">Step</span><span class="o">&gt;</span> <span class="p">{</span>
    <span class="k">let</span> <span class="nv">payload</span><span class="p">:</span> <span class="p">[</span><span class="kt">String</span><span class="p">:</span> <span class="kt">Any</span><span class="p">]</span>
<span class="p">}</span>

<span class="kd">func</span> <span class="nf">requestPermissions</span><span class="p">(</span><span class="n">_</span> <span class="nv">state</span><span class="p">:</span> <span class="kt">OnboardingState</span><span class="o">&lt;</span><span class="kt">Welcome</span><span class="o">&gt;</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">OnboardingState</span><span class="o">&lt;</span><span class="kt">Permissions</span><span class="o">&gt;</span> <span class="p">{</span>
    <span class="kt">OnboardingState</span><span class="o">&lt;</span><span class="kt">Permissions</span><span class="o">&gt;</span><span class="p">(</span><span class="nv">payload</span><span class="p">:</span> <span class="n">state</span><span class="o">.</span><span class="n">payload</span><span class="p">)</span>
<span class="p">}</span>

<span class="kd">func</span> <span class="nf">finish</span><span class="p">(</span><span class="n">_</span> <span class="nv">state</span><span class="p">:</span> <span class="kt">OnboardingState</span><span class="o">&lt;</span><span class="kt">Permissions</span><span class="o">&gt;</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">OnboardingState</span><span class="o">&lt;</span><span class="kt">Completed</span><span class="o">&gt;</span> <span class="p">{</span>
    <span class="kt">OnboardingState</span><span class="o">&lt;</span><span class="kt">Completed</span><span class="o">&gt;</span><span class="p">(</span><span class="nv">payload</span><span class="p">:</span> <span class="n">state</span><span class="o">.</span><span class="n">payload</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The compiler now knows you cannot jump straight from welcome to finish without granting permissions.</p>

<p>When you work with byte buffers, you can mark endian order or protocol framing to avoid mixing raw payloads with length-prefixed ones. In graphics, you can tag colors as <code class="language-plaintext highlighter-rouge">SRGB</code> or <code class="language-plaintext highlighter-rouge">DisplayP3</code> and make conversion explicit. In cryptography, you can tag keys as <code class="language-plaintext highlighter-rouge">PublicKey</code> versus <code class="language-plaintext highlighter-rouge">PrivateKey</code> to stop accidental misuse.</p>

<h2 id="designing-phantom-types-well">Designing phantom types well</h2>

<p>Keep tag types empty and name them for intent (<code class="language-plaintext highlighter-rouge">SafeSQL</code>, <code class="language-plaintext highlighter-rouge">Meters</code>, <code class="language-plaintext highlighter-rouge">WorldSpace</code>) so the purpose is clear at the call site. Lean on typealiases to keep code readable. Conform your wrapper (not the tag) to protocols like <code class="language-plaintext highlighter-rouge">Equatable</code> or <code class="language-plaintext highlighter-rouge">Codable</code> so you do not leak the implementation detail everywhere.</p>

<p>If the tag is widely used, wrap the boilerplate in small helpers so the call sites stay friendly:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">extension</span> <span class="kt">Tagged</span><span class="p">:</span> <span class="kt">Equatable</span> <span class="k">where</span> <span class="kt">Raw</span><span class="p">:</span> <span class="kt">Equatable</span> <span class="p">{}</span>
<span class="kd">extension</span> <span class="kt">Tagged</span><span class="p">:</span> <span class="kt">Hashable</span> <span class="k">where</span> <span class="kt">Raw</span><span class="p">:</span> <span class="kt">Hashable</span> <span class="p">{}</span>

<span class="kd">extension</span> <span class="kt">Tagged</span> <span class="p">{</span>
    <span class="k">var</span> <span class="nv">description</span><span class="p">:</span> <span class="kt">String</span> <span class="p">{</span> <span class="s">"</span><span class="se">\(</span><span class="n">raw</span><span class="se">)</span><span class="s">"</span> <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>You can then add conveniences like <code class="language-plaintext highlighter-rouge">init(uuid: UUID)</code> or <code class="language-plaintext highlighter-rouge">var asUUID: UUID</code> for specific tags. This keeps the API simple for newcomers while preserving the safety.</p>

<h2 id="pitfalls-to-avoid">Pitfalls to avoid</h2>

<p>It is tempting to tag everything, but overuse makes APIs noisy. Save phantom types for places where mixing values would be a real bug. Do not force tags into public protocols unless you want consumers to depend on them. Keep initializers narrow (for example, <code class="language-plaintext highlighter-rouge">init(rawValue:)</code> on the wrapper) so callers cannot bypass the validation you intend.</p>

<p>Another common footgun is forgetting to set the tag when bridging from other layers. If you parse JSON IDs as plain <code class="language-plaintext highlighter-rouge">UUID</code>, wrap them immediately into <code class="language-plaintext highlighter-rouge">Tagged&lt;UserIDTag, UUID&gt;</code> near the boundary so untagged values never flow inward. Keep interop helpers close to your decoding layer to avoid repeating conversions. Beginners can treat this as “tag on entry, untag on exit.”</p>

<h2 id="testing-phantom-heavy-code">Testing phantom-heavy code</h2>

<p>Test the compiler as well as the runtime behavior. Keep a tiny snippet that must fail to compile:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">enum</span> <span class="kt">RawSQL</span> <span class="p">{}</span>
<span class="kd">enum</span> <span class="kt">SafeSQL</span> <span class="p">{}</span>
<span class="kd">typealias</span> <span class="kt">SQL</span><span class="o">&lt;</span><span class="kt">Flavor</span><span class="o">&gt;</span> <span class="o">=</span> <span class="kt">Tagged</span><span class="o">&lt;</span><span class="kt">Flavor</span><span class="p">,</span> <span class="kt">String</span><span class="o">&gt;</span>

<span class="k">let</span> <span class="nv">bad</span><span class="p">:</span> <span class="kt">SQL</span><span class="o">&lt;</span><span class="kt">RawSQL</span><span class="o">&gt;</span> <span class="o">=</span> <span class="kt">SQL</span><span class="o">&lt;</span><span class="kt">SafeSQL</span><span class="o">&gt;</span><span class="p">(</span><span class="s">"sanitized"</span><span class="p">)</span> <span class="c1">// expected-error: cannot convert value</span>
</code></pre></div></div>

<p>If this ever compiles, you know a guard slipped. In regular unit tests, assert the happy path and a negative path:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">func</span> <span class="nf">test_execute_allows_only_safe_sql</span><span class="p">()</span> <span class="p">{</span>
    <span class="kd">enum</span> <span class="kt">RawSQL</span> <span class="p">{}</span>
    <span class="kd">enum</span> <span class="kt">SafeSQL</span> <span class="p">{}</span>
    <span class="kd">typealias</span> <span class="kt">SQL</span><span class="o">&lt;</span><span class="kt">Flavor</span><span class="o">&gt;</span> <span class="o">=</span> <span class="kt">Tagged</span><span class="o">&lt;</span><span class="kt">Flavor</span><span class="p">,</span> <span class="kt">String</span><span class="o">&gt;</span>

    <span class="kd">func</span> <span class="nf">execute</span><span class="p">(</span><span class="n">_</span> <span class="nv">query</span><span class="p">:</span> <span class="kt">SQL</span><span class="o">&lt;</span><span class="kt">SafeSQL</span><span class="o">&gt;</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">String</span> <span class="p">{</span> <span class="n">query</span><span class="o">.</span><span class="n">raw</span> <span class="p">}</span>

    <span class="k">let</span> <span class="nv">safe</span> <span class="o">=</span> <span class="kt">SQL</span><span class="o">&lt;</span><span class="kt">SafeSQL</span><span class="o">&gt;</span><span class="p">(</span><span class="s">"SELECT 1"</span><span class="p">)</span>
    <span class="kt">XCTAssertEqual</span><span class="p">(</span><span class="nf">execute</span><span class="p">(</span><span class="n">safe</span><span class="p">),</span> <span class="s">"SELECT 1"</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>

<p>For integration points, tag at the boundary:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">struct</span> <span class="kt">UserIDTag</span> <span class="p">{}</span>
<span class="kd">typealias</span> <span class="kt">UserID</span> <span class="o">=</span> <span class="kt">Tagged</span><span class="o">&lt;</span><span class="kt">UserIDTag</span><span class="p">,</span> <span class="kt">UUID</span><span class="o">&gt;</span>

<span class="kd">func</span> <span class="nf">decodeUserID</span><span class="p">(</span><span class="n">from</span> <span class="nv">json</span><span class="p">:</span> <span class="p">[</span><span class="kt">String</span><span class="p">:</span> <span class="kt">Any</span><span class="p">])</span> <span class="o">-&gt;</span> <span class="kt">UserID</span><span class="p">?</span> <span class="p">{</span>
    <span class="k">guard</span> <span class="k">let</span> <span class="nv">raw</span> <span class="o">=</span> <span class="n">json</span><span class="p">[</span><span class="s">"id"</span><span class="p">]</span> <span class="k">as?</span> <span class="kt">String</span><span class="p">,</span> <span class="k">let</span> <span class="nv">uuid</span> <span class="o">=</span> <span class="kt">UUID</span><span class="p">(</span><span class="nv">uuidString</span><span class="p">:</span> <span class="n">raw</span><span class="p">)</span> <span class="k">else</span> <span class="p">{</span> <span class="k">return</span> <span class="kc">nil</span> <span class="p">}</span>
    <span class="k">return</span> <span class="kt">UserID</span><span class="p">(</span><span class="n">uuid</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Once tagged, untagged values never enter the core. Performance is normally a non-issue: the tag is compile-time only, the wrapper is a thin shell, and the optimizer inlines the forwarding. Measure only if you put phantom wrappers inside tight loops; otherwise treat them as free safety.</p>

<h2 id="when-to-reach-for-phantom-types">When to reach for phantom types</h2>

<p>Reach for phantom types when you want the compiler to stop domain mix-ups: units (meters versus feet), coordinate spaces (view versus world), auth state (guest versus authenticated), or data trust levels (raw versus sanitized). They shine when runtime checks would be easy to forget or noisy, and when you want the safety without extra runtime cost. Avoid them if the extra generic parameter makes APIs harder to read, or if a plain enum or a dedicated struct communicates the rule clearly enough.</p>

<p>Phantom types are a gentle way to make invalid states unrepresentable in Swift. They turn assumptions into code the compiler enforces. Start with one hotspot (IDs, units, raw SQL), tag it, push that tag through your pipeline, and only expand when the safety win is clear.</p>]]></content><author><name>Deya Elkhawaldeh</name></author><category term="Development" /><category term="iOS" /><category term="Programming" /><category term="Swift" /><category term="Swift" /><category term="Type Safety" /><category term="Generics" /><category term="Phantom Types" /><summary type="html"><![CDATA[Phantom types let you tuck extra meaning into Swift’s type system without changing anything at runtime. They live in a generic parameter purely for compile-time checks, which means you can stop category mistakes (mixing meters with feet or safe SQL with raw text) while keeping zero overhead. If you are new to Swift’s type system, think of phantom types as labels the compiler reads, not values your app carries around.]]></summary></entry><entry><title type="html">Setting Up a New macOS Device: My Essential Tweaks</title><link href="https://swiftbydeya.com/mac-setup/" rel="alternate" type="text/html" title="Setting Up a New macOS Device: My Essential Tweaks" /><published>2025-08-29T00:00:00+03:00</published><updated>2025-08-29T00:00:00+03:00</updated><id>https://swiftbydeya.com/setup</id><content type="html" xml:base="https://swiftbydeya.com/mac-setup/"><![CDATA[<p>Getting a new Mac is always exciting, but the first few hours are usually spent setting things up just the way you like them. Here’s a collection of my favorite customizations, terminal tweaks, and productivity hacks that I always apply on a fresh macOS installation.</p>

<!--more-->

<!-- _includes/centered-image.html -->
<div class="centered-image">
  <img src="../images/covers/setup_full.webp" alt="Macos Setup" class="centered-image__img" width="960" height="1568" />
  <p align="center">
    <em>Macos Setup</em>
  </p>
</div>

<h2 id="personalizing-the-dock">Personalizing the Dock</h2>

<p>The Dock is where your most-used apps live, but sometimes you want to create a little breathing room between groups of apps. That’s where <strong>empty spacer tiles</strong> come in.</p>

<p>To add one:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>defaults write com.apple.dock persistent-apps -array-add '{"tile-type"="spacer-tile";}'; killall Dock
</code></pre></div></div>

<!-- _includes/centered-image.html -->
<div class="centered-image">
  <img src="/images/posts/dock.png" alt="" class="centered-image__img" />
  <p align="center">
    <em>Macos Dock With Spaces</em>
  </p>
</div>

<p>Each time you run the command, it inserts a blank space in your Dock, and you can rearrange them as needed.</p>

<hr />

<h2 id="preventing-sleep-during-long-tasks">Preventing Sleep During Long Tasks</h2>

<p>Sometimes you need your Mac awake for hours, like when running long builds, downloads, or data migrations. The simplest approach:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>caffeinate -disu
</code></pre></div></div>

<ul>
  <li><code class="language-plaintext highlighter-rouge">caffeinate</code> uses a <strong>single dash (<code class="language-plaintext highlighter-rouge">-</code>)</strong> followed by one or more flags, not <code class="language-plaintext highlighter-rouge">--</code>.</li>
  <li>The flags can be combined, so <code class="language-plaintext highlighter-rouge">-disu</code> is equivalent to <code class="language-plaintext highlighter-rouge">-d -i -s -u</code>.</li>
</ul>

<p>Here’s what each flag does:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">-d</code> → Prevent display from sleeping</li>
  <li><code class="language-plaintext highlighter-rouge">-i</code> → Prevent system idle sleep</li>
  <li><code class="language-plaintext highlighter-rouge">-s</code> → Prevent sleep when running on AC power</li>
  <li><code class="language-plaintext highlighter-rouge">-u</code> → Declare user activity (prevents display from dimming)</li>
</ul>

<p>⚡ <code class="language-plaintext highlighter-rouge">--disu</code> won’t work, because <code class="language-plaintext highlighter-rouge">caffeinate</code> doesn’t support GNU-style long flags.</p>

<p>As long as the <code class="language-plaintext highlighter-rouge">caffeinate</code> process runs, your Mac stays awake.</p>

<p>💡 <strong>Tip:</strong> Wrap <code class="language-plaintext highlighter-rouge">caffeinate</code> around a command to keep your Mac awake only while that process runs. Example:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>caffeinate -i xcodebuild
</code></pre></div></div>

<p>This way, the Mac automatically returns to normal sleep behavior when the task finishes.</p>

<hr />

<h2 id="terminal-tweaks">Terminal Tweaks</h2>

<p>Out of the box, macOS Terminal is functional, but a little bland. A few tweaks make it a more comfortable workspace.</p>

<p>Check your shell:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>echo $SHELL
</code></pre></div></div>

<p>Most modern Macs default to Zsh (<code class="language-plaintext highlighter-rouge">/bin/zsh</code>). To customize your prompt, add this to <code class="language-plaintext highlighter-rouge">~/.zshrc</code>:</p>

<div class="language-zsh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">PS1</span><span class="o">=</span><span class="s2">"%n %1~ %# "</span>
</code></pre></div></div>

<p>Reload with:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>source ~/.zshrc
</code></pre></div></div>

<p>This gives you a clean, informative prompt with your username and current directory. Pair it with a nice terminal theme (Solarized, Dracula, or Catppuccin are great choices), and you’ll have a much more pleasant developer experience.</p>

<p>💡 <strong>Extra ideas:</strong></p>

<ul>
  <li>Install <a href="https://iterm2.com/">iTerm2</a> for split panes, better search, and more shortcuts.</li>
  <li>Use <a href="https://brew.sh/">Homebrew</a> to install developer tools with one command.</li>
  <li>Add plugins like <code class="language-plaintext highlighter-rouge">zsh-autosuggestions</code> and <code class="language-plaintext highlighter-rouge">zsh-syntax-highlighting</code> for a smoother typing flow.</li>
</ul>

<hr />

<h2 id="making-typing-snappier">Making Typing Snappier</h2>

<p>If you type fast, macOS’s default key repeat rates feel sluggish. Speed them up with:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>defaults write -g InitialKeyRepeat -int 10
defaults write -g KeyRepeat -int 1
</code></pre></div></div>

<p>⚠️ <strong>Warning</strong>: Don’t set <code class="language-plaintext highlighter-rouge">InitialKeyRepeat</code> below <code class="language-plaintext highlighter-rouge">10</code>, or you risk getting stuck at the login screen and needing accessibility features to log in.</p>

<p>Restart your Mac for changes to take effect.</p>

<p>And if you hate the popup menu that appears when holding a key, instead of just repeating it, disable it:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>defaults write -g ApplePressAndHoldEnabled -bool false
</code></pre></div></div>

<p>💡 <strong>Tip:</strong> If you regularly type in multiple languages, you may prefer keeping the popup enabled, since it helps you access accented characters quickly.</p>

<hr />

<h2 id="finder-tweaks">Finder Tweaks</h2>

<p>Finder can be friendlier with a few quick adjustments. For example, enable the <strong>status bar</strong> (bottom of Finder windows), so you can always see the number of items and available disk space.</p>

<p>In Finder:</p>

<ul>
  <li>Go to <strong>View &gt; Show Status Bar</strong></li>
</ul>

<p>I also recommend enabling <strong>Show Path Bar</strong> (View &gt; Show Path Bar), so you can see exactly where you are in the filesystem hierarchy.</p>

<p>💡 <strong>Extra ideas:</strong></p>

<ul>
  <li>Enable <strong>View &gt; Show Tab Bar</strong> to work with multiple Finder tabs like in a browser.</li>
  <li>Use <strong>View &gt; As Columns</strong> for a fast, hierarchical view when navigating deep folders.</li>
  <li>Add common folders to the sidebar for one-click access.</li>
</ul>

<p>⌘+1 → Icon View
⌘+2 → List View
⌘+3 → Column View
⌘+4 → Gallery View</p>

<p>Note: Double clicking vertical line expand the pane to max needed width.</p>

<!-- _includes/centered-image.html -->
<div class="centered-image">
  <img src="/images/posts/resize.gif" alt="" class="centered-image__img" />
  <p align="center">
    <em>Macos Dock With Spaces</em>
  </p>
</div>

<hr />

<h2 id="git-worktrees-for-clean-development">Git Worktrees for Clean Development</h2>

<p>When working with multiple Git branches, I prefer using <strong>worktrees</strong> instead of constantly checking branches in and out.</p>

<p>Example:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git worktree add ../wt/app_name/feature_two feature_two
</code></pre></div></div>

<p>This creates a new working directory with the branch <code class="language-plaintext highlighter-rouge">feature_two</code> checked out, perfect for parallel development without polluting your main repo folder.</p>

<p>💡 <strong>Extra ideas:</strong></p>

<ul>
  <li>Use <code class="language-plaintext highlighter-rouge">git worktree list</code> to see all your active worktrees.</li>
  <li>Use <code class="language-plaintext highlighter-rouge">git worktree remove path/to/worktree</code> to clean up old ones.</li>
  <li>Combine worktrees with a <code class="language-plaintext highlighter-rouge">~/Projects/wt/</code> folder for organized, isolated development environments.</li>
</ul>

<hr />

<h2 id="smarter-window-management-with-rectangle">Smarter Window Management with Rectangle</h2>

<p>macOS has basic window snapping, but if you want full control over where apps go on your screen, <a href="https://rectangleapp.com/">Rectangle</a> is the go-to tool. It lets you quickly move and resize windows using customizable keyboard shortcuts.</p>

<p>💡 <strong>Why it’s great:</strong></p>

<ul>
  <li>Snap windows to halves, thirds, or quarters of the screen.</li>
  <li>Move windows between displays instantly.</li>
  <li>Create advanced layouts that fit your workflow.</li>
</ul>

<p>To take it further, you can bind shortcuts to the <strong>numeric keypad</strong> for intuitive tiling. For example, treat your screen like a grid:</p>

<ul>
  <li><strong>Numpad 7 / 8 / 9</strong> → Top left, top center, top right</li>
  <li><strong>Numpad 4 / 5 / 6</strong> → Left, center, right</li>
  <li><strong>Numpad 1 / 2 / 3</strong> → Bottom left, bottom center, bottom right</li>
</ul>

<p>Or use <strong>8-section tiling</strong> with bindings like:</p>

<ul>
  <li><strong>Numpad 8</strong> → Top half</li>
  <li><strong>Numpad 2</strong> → Bottom half</li>
  <li><strong>Numpad 4</strong> → Left half</li>
  <li><strong>Numpad 6</strong> → Right half</li>
  <li><strong>Numpad 7 / 9 / 1 / 3</strong> → Corners</li>
</ul>

<p>This way, you can manage windows fluidly with one hand, without dragging them around.</p>]]></content><author><name>Deya Elkhawaldeh</name></author><category term="Development" /><category term="iOS" /><category term="Programming" /><category term="Swift" /><category term="Development" /><category term="iOS" /><category term="Programming" /><category term="Swift" /><summary type="html"><![CDATA[Getting a new Mac is always exciting, but the first few hours are usually spent setting things up just the way you like them. Here’s a collection of my favorite customizations, terminal tweaks, and productivity hacks that I always apply on a fresh macOS installation.]]></summary></entry><entry><title type="html">Zombies In Swift</title><link href="https://swiftbydeya.com/swift-zombies/" rel="alternate" type="text/html" title="Zombies In Swift" /><published>2024-09-08T00:00:00+03:00</published><updated>2024-09-08T00:00:00+03:00</updated><id>https://swiftbydeya.com/zombies</id><content type="html" xml:base="https://swiftbydeya.com/swift-zombies/"><![CDATA[<p>In Swift, <strong>zombie objects</strong> is a debugging term for <strong>use-after-free</strong> bugs: your code tries to access an instance that has already been deallocated. Swift’s <strong>Automatic Reference Counting (ARC)</strong> frees objects when their strong reference count reaches zero, but mistakes like using <code class="language-plaintext highlighter-rouge">unowned</code> when the object might die first, misusing unsafe pointers/<code class="language-plaintext highlighter-rouge">Unmanaged</code>, or racy deallocation on another thread can leave you with a dangling reference.</p>

<!--more-->

<p>With Xcode’s “Enable Zombie Objects,” deallocated Objective-C/bridged objects are replaced by special <strong>zombie proxies</strong> that log “message sent to deallocated instance,” helping you catch the access during debugging. In production, there are no zombies just crashes such as <code class="language-plaintext highlighter-rouge">EXC_BAD_ACCESS</code> or “attempted to read an unowned reference but object was already deallocated.”</p>

<p>Swift’s safety features prevent many memory errors, but understanding ARC and reference semantics is still essential for robust apps.</p>

<!-- _includes/centered-image.html -->
<div class="centered-image">
  <img src="../images/covers/zombies_full.webp" alt="Swift Zombies" class="centered-image__img" width="960" height="1568" />
  <p align="center">
    <em>Swift Zombies</em>
  </p>
</div>

<p>Zombie situations arise when some code path still holds a reference to an object that ARC already destroyed. This is particularly risky with <strong><code class="language-plaintext highlighter-rouge">unowned</code> references</strong> which never become <code class="language-plaintext highlighter-rouge">nil</code>, and when dealing with <strong><code class="language-plaintext highlighter-rouge">UnsafePointer</code></strong> or <strong><code class="language-plaintext highlighter-rouge">Unmanaged</code></strong> APIs. Touching such memory triggers immediate crashes or undefined behavior.</p>

<p>To avoid this class of bugs, prefer safe references (<code class="language-plaintext highlighter-rouge">weak</code> where appropriate), be cautious with unsafe APIs, and lean on Xcode’s diagnostics to surface mistakes early.</p>

<h2 id="what-are-zombies-in-swift">What Are Zombies in Swift?</h2>

<p>A “zombie” is not a real runtime type in Swift apps; it’s a <strong>debugging aid</strong>. ARC deallocates objects when their strong count hits zero. If you later access that memory through a stale reference, you’ve created a <strong>use-after-free</strong>. With zombies enabled, many Cocoa/Cocoa Touch objects (Objective-C or bridged Foundation/UIKit types) are turned into proxies that log a clear error when touched. Pure Swift classes don’t become zombies; instead, you’ll typically see a trap such as an <strong>unowned access crash</strong> or <strong><code class="language-plaintext highlighter-rouge">EXC_BAD_ACCESS</code></strong>.</p>

<h3 id="retain-cycles-and-arc">Retain Cycles and ARC</h3>

<p>ARC tracks strong references and deallocates when the count is zero. <strong>Retain cycles</strong> (two+ objects holding each other strongly) prevent deallocation and cause <strong>memory leaks</strong>, not zombies. Developers sometimes try to “fix” a cycle by switching to <code class="language-plaintext highlighter-rouge">unowned</code> incorrectly; that may remove the leak but can introduce a <strong>use-after-free</strong> if lifetimes don’t strictly align. The lesson: fix cycles correctly (usually with <code class="language-plaintext highlighter-rouge">weak</code>), not by guessing with <code class="language-plaintext highlighter-rouge">unowned</code>.</p>

<h2 id="causes-of-zombie-objects-in-swift">Causes of Zombie Objects in Swift</h2>

<h3 id="1-misused-unowned">1. Misused <code class="language-plaintext highlighter-rouge">unowned</code></h3>

<p><code class="language-plaintext highlighter-rouge">unowned</code> asserts that the referenced object <strong>always</strong> outlives the holder. If that’s wrong, any access crashes at runtime.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="kt">Parent</span> <span class="p">{</span>
    <span class="k">var</span> <span class="nv">child</span><span class="p">:</span> <span class="kt">Child</span><span class="p">?</span>
    <span class="nf">init</span><span class="p">()</span> <span class="p">{</span> <span class="n">child</span> <span class="o">=</span> <span class="kt">Child</span><span class="p">(</span><span class="nv">parent</span><span class="p">:</span> <span class="k">self</span><span class="p">)</span> <span class="p">}</span>
<span class="p">}</span>

<span class="kd">class</span> <span class="kt">Child</span> <span class="p">{</span>
    <span class="k">unowned</span> <span class="k">let</span> <span class="nv">parent</span><span class="p">:</span> <span class="kt">Parent</span> <span class="c1">// crashes if parent deallocates first</span>
    <span class="nf">init</span><span class="p">(</span><span class="nv">parent</span><span class="p">:</span> <span class="kt">Parent</span><span class="p">)</span> <span class="p">{</span> <span class="k">self</span><span class="o">.</span><span class="n">parent</span> <span class="o">=</span> <span class="n">parent</span> <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Use <code class="language-plaintext highlighter-rouge">unowned</code> only when the lifetime relationship is strict and provable (e.g., bidirectional relationships where one side truly owns the other and deallocates last). Otherwise, prefer <code class="language-plaintext highlighter-rouge">weak</code>.</p>

<h3 id="2-misuse-of-unsafe-pointers--unmanaged">2. Misuse of Unsafe Pointers / <code class="language-plaintext highlighter-rouge">Unmanaged</code></h3>

<p><code class="language-plaintext highlighter-rouge">UnsafePointer</code>/<code class="language-plaintext highlighter-rouge">UnsafeMutablePointer</code> and <code class="language-plaintext highlighter-rouge">Unmanaged</code> bypass ARC checks. A pointer that outlives the pointee—or the wrong <code class="language-plaintext highlighter-rouge">takeRetainedValue</code>/<code class="language-plaintext highlighter-rouge">takeUnretainedValue</code> choice—can produce a use-after-free. When bridging to Core Foundation, be meticulous about ownership conventions.</p>

<h3 id="3-over-releasing-in-bridgedlegacy-code">3. Over-releasing in Bridged/Legacy Code</h3>

<p>Manual memory management or incorrect bridging in Objective-C/C code can over-release objects. Swift code that later touches them will crash.</p>

<h3 id="4-concurrency-and-races">4. Concurrency and Races</h3>

<p>A background task may keep a reference while another thread (or task) causes deallocation. Without proper synchronization or lifetime management, an access can land after deinit.</p>

<h2 id="why-zombies-are-bad">Why Zombies Are Bad</h2>

<ul>
  <li><strong>Crashes &amp; Data Corruption</strong>: Use-after-free is undefined behavior; expect hard crashes or corrupted state.</li>
  <li><strong>Heisenbugs</strong>: Timing-dependent failures are intermittent and hard to reproduce.</li>
  <li><strong>False Fixes</strong>: “Fixing” leaks by switching to <code class="language-plaintext highlighter-rouge">unowned</code> can trade a leak for a crash. Prefer structural fixes (proper ownership, <code class="language-plaintext highlighter-rouge">weak</code>).</li>
</ul>

<blockquote>
  <p>Note: Zombies don’t <strong>cause</strong> leaks; leaks happen when objects <strong>don’t</strong> deallocate (e.g., due to retain cycles). Zombies help you detect accesses <strong>after</strong> deallocation.</p>
</blockquote>

<h2 id="how-to-avoid-zombies">How to Avoid Zombies</h2>

<h3 id="1-use-weakunowned-correctly">1. Use Weak/Unowned Correctly</h3>

<p>Prefer <strong><code class="language-plaintext highlighter-rouge">weak</code></strong> when the referenced object may deallocate first. It auto-nilifies and avoids crashes.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">protocol</span> <span class="kt">DataSourceDelegate</span><span class="p">:</span> <span class="kt">AnyObject</span> <span class="p">{</span>
    <span class="kd">func</span> <span class="nf">didUpdate</span><span class="p">()</span>
<span class="p">}</span>

<span class="kd">class</span> <span class="kt">DataSource</span> <span class="p">{</span>
    <span class="k">weak</span> <span class="k">var</span> <span class="nv">delegate</span><span class="p">:</span> <span class="kt">DataSourceDelegate</span><span class="p">?</span>
<span class="p">}</span>

<span class="kd">class</span> <span class="kt">ViewController</span><span class="p">:</span> <span class="kt">UIViewController</span><span class="p">,</span> <span class="kt">DataSourceDelegate</span> <span class="p">{</span>
    <span class="kd">private</span> <span class="k">var</span> <span class="nv">dataSource</span> <span class="o">=</span> <span class="kt">DataSource</span><span class="p">()</span>
    <span class="k">override</span> <span class="kd">func</span> <span class="nf">viewDidLoad</span><span class="p">()</span> <span class="p">{</span>
        <span class="k">super</span><span class="o">.</span><span class="nf">viewDidLoad</span><span class="p">()</span>
        <span class="n">dataSource</span><span class="o">.</span><span class="n">delegate</span> <span class="o">=</span> <span class="k">self</span>
    <span class="p">}</span>
    <span class="kd">func</span> <span class="nf">didUpdate</span><span class="p">()</span> <span class="p">{</span> <span class="cm">/* ... */</span> <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Reserve <strong><code class="language-plaintext highlighter-rouge">unowned</code></strong> for relationships where the owner truly outlives the dependent for the entire lifetime of the reference.</p>

<h3 id="2-break-strong-reference-cycles-in-closures">2. Break Strong Reference Cycles in Closures</h3>

<p>Closures capture strongly by default. Use capture lists to avoid cycles and crashes from stale <code class="language-plaintext highlighter-rouge">self</code>:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">final</span> <span class="kd">class</span> <span class="kt">Worker</span> <span class="p">{</span>
    <span class="k">var</span> <span class="nv">onFinish</span><span class="p">:</span> <span class="p">(()</span> <span class="o">-&gt;</span> <span class="kt">Void</span><span class="p">)?</span>

    <span class="kd">func</span> <span class="nf">start</span><span class="p">()</span> <span class="p">{</span>
        <span class="n">onFinish</span> <span class="o">=</span> <span class="p">{</span> <span class="p">[</span><span class="k">weak</span> <span class="k">self</span><span class="p">]</span> <span class="k">in</span>
            <span class="k">guard</span> <span class="k">let</span> <span class="nv">self</span> <span class="k">else</span> <span class="p">{</span> <span class="k">return</span> <span class="p">}</span> <span class="c1">// self may be gone; safely bail</span>
            <span class="k">self</span><span class="o">.</span><span class="nf">report</span><span class="p">()</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="kd">private</span> <span class="kd">func</span> <span class="nf">report</span><span class="p">()</span> <span class="p">{</span> <span class="cm">/* ... */</span> <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<h3 id="3-enable-zombie-detection-when-debugging">3. Enable Zombie Detection (When Debugging)</h3>

<ul>
  <li><strong>Product → Scheme → Edit Scheme → Run (Debug) → Diagnostics → Enable Zombie Objects</strong>.
Use it to catch <strong>message-sent-to-deallocated-instance</strong> issues with Objective-C/bridged objects. Zombies dramatically increase memory usage—<strong>only enable during targeted debugging</strong>, not in release builds.</li>
</ul>

<h3 id="4-use-the-right-sanitizers--tools">4. Use the Right Sanitizers &amp; Tools</h3>

<ul>
  <li><strong>Memory Graph Debugger</strong>: Spot retain cycles straight from Xcode while paused.</li>
  <li><strong>Address Sanitizer (ASan)</strong>: Detects many use-after-free and buffer errors in Swift/C/Obj-C.</li>
  <li><strong>Thread Sanitizer (TSan)</strong>: Surfaces race conditions that can lead to premature deallocation.</li>
  <li><strong>Instruments → Leaks/Allocations</strong>: Track growth, find cycles, and inspect lifetimes.</li>
  <li><strong>Guard Malloc / Malloc Scribble</strong>: Poison/fill freed memory to expose invalid accesses sooner.</li>
</ul>

<h3 id="5-be-careful-with-asyncbackground-work">5. Be Careful with Async/Background Work</h3>

<p>Cancel work or clear callbacks when owners deinit. Example with GCD:</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">final</span> <span class="kd">class</span> <span class="kt">Loader</span> <span class="p">{</span>
    <span class="kd">private</span> <span class="k">let</span> <span class="nv">queue</span> <span class="o">=</span> <span class="kt">DispatchQueue</span><span class="p">(</span><span class="nv">label</span><span class="p">:</span> <span class="s">"loader"</span><span class="p">)</span>

    <span class="kd">func</span> <span class="nf">load</span><span class="p">(</span><span class="nv">completion</span><span class="p">:</span> <span class="kd">@escaping</span> <span class="p">(</span><span class="kt">Data</span><span class="p">?)</span> <span class="o">-&gt;</span> <span class="kt">Void</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">queue</span><span class="o">.</span><span class="k">async</span> <span class="p">{</span> <span class="p">[</span><span class="k">weak</span> <span class="k">self</span><span class="p">]</span> <span class="k">in</span>
            <span class="k">guard</span> <span class="k">self</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="k">else</span> <span class="p">{</span> <span class="k">return</span> <span class="p">}</span> <span class="c1">// owner gone; don't call back</span>
            <span class="c1">// ... do work ...</span>
            <span class="kt">DispatchQueue</span><span class="o">.</span><span class="n">main</span><span class="o">.</span><span class="k">async</span> <span class="p">{</span> <span class="p">[</span><span class="k">weak</span> <span class="k">self</span><span class="p">]</span> <span class="k">in</span>
                <span class="k">guard</span> <span class="k">self</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="k">else</span> <span class="p">{</span> <span class="k">return</span> <span class="p">}</span>
                <span class="nf">completion</span><span class="p">(</span><span class="kt">Data</span><span class="p">())</span>
            <span class="p">}</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<h2 id="conclusion">Conclusion</h2>

<p>Swift’s ARC removes much manual memory bookkeeping, but <strong>lifetime mistakes still happen</strong>. Treat “zombies” as the debugger’s name for <strong>use-after-free</strong>: avoid them by modeling ownership correctly, preferring <code class="language-plaintext highlighter-rouge">weak</code> over <code class="language-plaintext highlighter-rouge">unowned</code> unless lifetimes are guaranteed, steering clear of unsafe APIs unless necessary, and using Xcode’s diagnostics (Zombies, ASan, TSan, Memory Graph, Instruments). These practices keep your code safe, predictable, and crash-free.</p>

<p>Happy coding!</p>]]></content><author><name>Deya Elkhawaldeh</name></author><category term="Development" /><category term="iOS" /><category term="Programming" /><category term="Swift" /><category term="Development" /><category term="iOS" /><category term="Programming" /><category term="Swift" /><summary type="html"><![CDATA[In Swift, zombie objects is a debugging term for use-after-free bugs: your code tries to access an instance that has already been deallocated. Swift’s Automatic Reference Counting (ARC) frees objects when their strong reference count reaches zero, but mistakes like using unowned when the object might die first, misusing unsafe pointers/Unmanaged, or racy deallocation on another thread can leave you with a dangling reference.]]></summary></entry><entry><title type="html">Network Issues in MacBooks with Silicon Chips and Proposed Solutions</title><link href="https://swiftbydeya.com/solving-network-issues-on-macbooks-silicon-chips/" rel="alternate" type="text/html" title="Network Issues in MacBooks with Silicon Chips and Proposed Solutions" /><published>2024-02-02T00:00:00+03:00</published><updated>2024-09-08T00:00:00+03:00</updated><id>https://swiftbydeya.com/network-problems-on-silicon-chips</id><content type="html" xml:base="https://swiftbydeya.com/solving-network-issues-on-macbooks-silicon-chips/"><![CDATA[<p>As Apple’s Silicon processors have gained significant attention in the tech community, many users have reported significant stability and network performance issues. These problems are particularly noticeable when using Wi-Fi on the 2.4 GHz band or connecting via a USB dongle for LAN.</p>

<!--more-->
<p>Users have expressed concerns over the reliability of their devices, especially when it comes to maintaining stable connections. The shift from Intel to Apple Silicon has been met with enthusiasm due to the performance enhancements these processors offer, yet the transition has not been without its challenges. Many users have noted that while the overall performance of their Macs has improved, specific functionalities, particularly those related to network connectivity, have suffered.</p>

<!-- _includes/centered-image.html -->
<div class="centered-image">
  <img src="../images/covers/network_fix_full.webp" alt="Network Performance Issues" class="centered-image__img" width="960" height="1568" />
  <p align="center">
    <em>Network Performance Issues on M1/M2 MacBooks</em>
  </p>
</div>

<h2 id="overview-of-network-issues">Overview of Network Issues</h2>

<p>One common complaint among users is the sudden and prolonged drops in transfer rates, leading to frustrating experiences. These issues can often be attributed to the design of the network card in these devices, which may struggle to maintain consistent performance under certain conditions.</p>

<p>When operating on the 2.4 GHz band, devices with Silicon processors may experience exceptionally low transfer rates, sometimes as low as 0.5 Mbps 😱. This is significantly lower than expected, and while not all devices are necessarily affected, numerous complaints have surfaced online.</p>

<p>For instance, I experienced a connection that was about 30 times faster when using my MacBook Pro 2019 compared to the M1 Pro. Simple benchmarks using the <strong><code class="language-plaintext highlighter-rouge">networkQuality</code></strong> command revealed the following results:</p>

<h3 id="benchmark-results">Benchmark Results</h3>

<p><strong>M1 Pro Internet Speed:</strong></p>
<ul>
  <li><strong>Downlink:</strong> 0.568 Mbps</li>
  <li><strong>Uplink:</strong> 1.920 Mbps</li>
</ul>

<p><strong>Intel-based Internet Speed:</strong></p>
<ul>
  <li><strong>Downlink:</strong> 14.347 Mbps</li>
  <li><strong>Uplink:</strong> 4.175 Mbps</li>
</ul>

<p>Such results were shocking, especially considering I do not have fiber internet and rely on 4G. 🤣</p>

<p>Initially, I suspected that VPN or MDM settings on the M1 Pro were causing these significant speed drops. However, further research led me to useful findings that helped restore speed on my M1 Pro device.</p>

<h2 id="understanding-frequency-bands">Understanding Frequency Bands</h2>

<p>The 2.4 GHz band offers better coverage and penetration through walls but is more susceptible to interference from other devices and nearby Wi-Fi networks. This can lead to congestion and speed drops, especially in crowded areas. Conversely, the 5 GHz band is generally faster and less prone to interference, although it has a shorter range and may struggle to penetrate solid objects.</p>

<p>Additionally, connecting to a LAN via a USB dongle (especially when a monitor is connected to the same dongle) has been reported to cause network performance issues, complicating the challenges faced by users who rely on stable and high-speed connections.</p>

<h2 id="proposed-solutions">Proposed Solutions</h2>

<p>While these problems may seem daunting, there are several potential workarounds and solutions that users can consider:</p>

<h3 id="1-utilize-safe-mode-for-diagnostics">1. Utilize Safe Mode for Diagnostics</h3>

<p>When troubleshooting network performance issues on devices with Silicon processors, using the <strong><code class="language-plaintext highlighter-rouge">networkQuality</code></strong> command in safe mode can be a valuable diagnostic tool. Safe mode loads only essential components, allowing users to isolate potential software conflicts or third-party applications that may be impacting network performance. Running the <strong><code class="language-plaintext highlighter-rouge">networkQuality</code></strong> command in this environment can provide a clearer picture of the device’s network status.</p>

<h3 id="2-switch-to-the-5-ghz-band">2. Switch to the 5 GHz Band</h3>

<p>Switching to the 5 GHz band and disabling the 2.4 GHz network on your router can be an effective strategy for addressing performance issues. Additionally, using a 40 MHz channel width can help mitigate congestion and interference, resulting in improved network performance. This simple change can often make a noticeable difference in the reliability and speed of the Wi-Fi network for devices with Silicon processors.</p>

<h3 id="3-consider-wi-fi-over-usb-c-dongles">3. Consider Wi-Fi Over USB-C Dongles</h3>

<p>If you are using a USB-C dongle for network connectivity and experiencing issues, consider switching to Wi-Fi as an alternative. Assess the network quality using <strong><code class="language-plaintext highlighter-rouge">networkQuality</code></strong> to compare the performance of the USB-C dongle with that of the Wi-Fi connection. This approach can help identify specific issues related to the dongle or the network environment.</p>

<h3 id="4-disable-unused-network-features">4. Disable Unused Network Features</h3>

<p>Disabling unnecessary network features such as “Thunderbolt Bridge,” which allows high-speed data transfer between two Mac computers using Thunderbolt ports, may also help. Users have reported that disabling features they do not use can resolve network performance issues.</p>

<h2 id="conclusion">Conclusion</h2>

<p>It’s crucial to be aware of these potential network performance issues when using devices with Silicon processors. While not all devices may be affected, many users have shared similar complaints. I hope this information proves helpful for those navigating network performance challenges on Silicon powered devices.</p>

<p>By following the proposed solutions, users may find improved network stability and performance, enhancing their overall experience with these powerful machines.</p>]]></content><author><name>Deya Elkhawaldeh</name></author><category term="Development" /><category term="iOS" /><category term="Programming" /><category term="Swift" /><category term="Development" /><category term="iOS" /><category term="Programming" /><category term="Swift" /><summary type="html"><![CDATA[As Apple’s Silicon processors have gained significant attention in the tech community, many users have reported significant stability and network performance issues. These problems are particularly noticeable when using Wi-Fi on the 2.4 GHz band or connecting via a USB dongle for LAN.]]></summary></entry><entry><title type="html">Test Doubles In Swift</title><link href="https://swiftbydeya.com/test-doubles-in-swift/" rel="alternate" type="text/html" title="Test Doubles In Swift" /><published>2023-10-25T00:00:00+03:00</published><updated>2024-09-08T00:00:00+03:00</updated><id>https://swiftbydeya.com/test-doubles-in-swift</id><content type="html" xml:base="https://swiftbydeya.com/test-doubles-in-swift/"><![CDATA[<p>The term <strong>test doubles</strong> draws inspiration from <strong>stunt doubles</strong> in the movie industry, where a stunt double steps in to perform dangerous or complex tasks, allowing the actor to focus on their role. Similarly, in software testing, test doubles step in to replace real components, making testing simpler, faster, and more reliable.</p>

<!--more-->

<p>Testing is an essential part of software development, ensuring the correctness and reliability of our code. However, when we test systems with many dependencies—like databases, web services, or external APIs—writing reliable tests can become challenging. This is where test doubles come in handy.</p>

<!-- _includes/centered-image.html -->
<div class="centered-image">
  <img src="../images/covers/test_doubles_full.webp" alt="" class="centered-image__img" width="960" height="1568" />
  <p align="center">
    <em></em>
  </p>
</div>

<p>It’s important to understand the basics of unit testing, see this blog post <a href="https://swiftbydeya.com/gentle-introduction-to-unit-testing/">Gentle Introduction to Unit Testing</a>, especially since certain architectural patterns, like MVC can introduce complexities that make them less testable. Familiarity with the FIRST principles—Fast, Independent, Repeatable, Self-validating, and Timely—is crucial for writing effective tests. Additionally, it’s vital to recognize that flaky tests, which produce inconsistent results, can undermine the reliability of your testing suite and lead to wasted time and effort.</p>

<p>In this post, we’ll explore the different types of test doubles, their purpose, and practical examples. By the end, you’ll be able to confidently use them to create more effective, reliable tests.</p>

<h2 id="what-is-a-test-double">What is a Test Double?</h2>
<p>A <strong>test double</strong> is a substitute that stands in for a real dependency during testing. These dependencies, which can include external services, databases, or complex components, often introduce complexity that makes testing challenging. Test doubles enable us to isolate the code under test and concentrate on specific behaviors, resulting in more predictable and efficient tests. By using test doubles, we can create controlled environments that facilitate thorough testing without the overhead of managing real dependencies. This revision broadens the definition of a test double while maintaining clarity and focus on its purpose in testing.</p>

<p>Test doubles mimic the behavior of real dependencies, but they provide simplified or controlled implementations. By replacing real dependencies with test doubles, we create an environment where we control every interaction, avoiding side effects from external systems.</p>

<h3 id="types-of-test-doubles">Types of Test Doubles</h3>

<p>There are five common types of test doubles, each serving a distinct purpose:</p>

<ol>
  <li><strong>Dummy</strong></li>
  <li><strong>Fake</strong></li>
  <li><strong>Stub</strong></li>
  <li><strong>Spy</strong></li>
  <li><strong>Mock</strong></li>
</ol>

<p>Let’s dive into each of these with practical Swift examples. We’ll use a sample <code class="language-plaintext highlighter-rouge">UserManager</code> class that has dependencies carefully picked for demonstrating all types of test doubles, it may be needed to create multiple test doubles of different types for each dependency in some cases, this depends on the test cases needs, but in this example, only one test double is created for each dependency, for demo purpose.</p>

<p><code class="language-plaintext highlighter-rouge">UserManager</code> also uses default values that can be changed, this is dependency injection by init.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// UserManager.swift</span>
<span class="kd">class</span> <span class="kt">UserManager</span> <span class="p">{</span>
    <span class="k">let</span> <span class="nv">loggerService</span><span class="p">:</span> <span class="kt">LoggerService</span>
    <span class="k">let</span> <span class="nv">cacheService</span><span class="p">:</span> <span class="kt">CacheService</span>
    <span class="k">let</span> <span class="nv">database</span><span class="p">:</span> <span class="kt">DatabaseHelper</span>
    <span class="k">let</span> <span class="nv">securityHelper</span><span class="p">:</span> <span class="kt">SecurityHelper</span>
    <span class="k">let</span> <span class="nv">notificationService</span><span class="p">:</span> <span class="kt">NotificationService</span>

    <span class="nf">init</span><span class="p">(</span>
        <span class="nv">logger</span><span class="p">:</span> <span class="kt">LoggerService</span> <span class="o">=</span> <span class="kt">LoggerServiceImpl</span><span class="p">(),</span>
        <span class="nv">cache</span><span class="p">:</span> <span class="kt">CacheService</span> <span class="o">=</span> <span class="kt">CacheServiceImpl</span><span class="p">(),</span>
        <span class="nv">database</span><span class="p">:</span> <span class="kt">DatabaseHelper</span> <span class="o">=</span> <span class="kt">DatabaseHelperImpl</span><span class="p">(),</span>
        <span class="nv">securityHelper</span><span class="p">:</span> <span class="kt">SecurityHelper</span> <span class="o">=</span> <span class="kt">SecurityHelperImpl</span><span class="p">(),</span>
        <span class="nv">notificationService</span><span class="p">:</span> <span class="kt">NotificationService</span> <span class="o">=</span> <span class="kt">NotificationServiceImpl</span><span class="p">()</span>
    <span class="p">)</span> <span class="p">{</span>
        <span class="k">self</span><span class="o">.</span><span class="n">loggerService</span> <span class="o">=</span> <span class="n">logger</span>
        <span class="k">self</span><span class="o">.</span><span class="n">cacheService</span> <span class="o">=</span> <span class="n">cache</span>
        <span class="k">self</span><span class="o">.</span><span class="n">database</span> <span class="o">=</span> <span class="n">database</span>
        <span class="k">self</span><span class="o">.</span><span class="n">securityHelper</span> <span class="o">=</span> <span class="n">securityHelper</span>
        <span class="k">self</span><span class="o">.</span><span class="n">notificationService</span> <span class="o">=</span> <span class="n">notificationService</span>
    <span class="p">}</span>
    
    <span class="kd">func</span> <span class="nf">authenticate</span><span class="p">(</span><span class="nv">username</span><span class="p">:</span> <span class="kt">String</span><span class="p">,</span> <span class="nv">password</span><span class="p">:</span> <span class="kt">String</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">Bool</span> <span class="p">{</span>
        <span class="n">loggerService</span><span class="o">.</span><span class="nf">log</span><span class="p">(</span><span class="s">"Authenticating user </span><span class="se">\(</span><span class="n">username</span><span class="se">)</span><span class="s"> </span><span class="se">\(</span><span class="n">password</span><span class="se">)</span><span class="s">"</span><span class="p">)</span>
        
        <span class="c1">// Check cache first</span>
        <span class="k">if</span> <span class="k">let</span> <span class="nv">cachedPasswordHash</span> <span class="o">=</span> <span class="n">cacheService</span><span class="o">.</span><span class="nf">get</span><span class="p">(</span><span class="n">username</span><span class="p">)</span> <span class="p">{</span>
            <span class="n">loggerService</span><span class="o">.</span><span class="nf">log</span><span class="p">(</span><span class="s">"Cache hit for user </span><span class="se">\(</span><span class="n">username</span><span class="se">)</span><span class="s">"</span><span class="p">)</span>
            <span class="k">let</span> <span class="nv">success</span> <span class="o">=</span> <span class="n">cachedPasswordHash</span> <span class="o">==</span> <span class="n">password</span><span class="o">.</span><span class="nf">hashed</span><span class="p">()</span>
            <span class="n">securityHelper</span><span class="o">.</span><span class="nf">recordAuthenticationAttempt</span><span class="p">(</span><span class="nv">username</span><span class="p">:</span> <span class="n">username</span><span class="p">,</span> <span class="nv">success</span><span class="p">:</span> <span class="n">success</span><span class="p">)</span>
            <span class="n">notificationService</span><span class="o">.</span><span class="nf">sendAuthenticationEmail</span><span class="p">(</span><span class="nv">username</span><span class="p">:</span> <span class="n">username</span><span class="p">,</span> <span class="nv">success</span><span class="p">:</span> <span class="n">success</span><span class="p">)</span>
            <span class="k">return</span> <span class="n">success</span>
        <span class="p">}</span>
        
        <span class="c1">// Fetch from database if not in cache</span>
        <span class="n">loggerService</span><span class="o">.</span><span class="nf">log</span><span class="p">(</span><span class="s">"Cache miss for user </span><span class="se">\(</span><span class="n">username</span><span class="se">)</span><span class="s">"</span><span class="p">)</span>
        <span class="k">if</span> <span class="k">let</span> <span class="nv">passwordHash</span> <span class="o">=</span> <span class="n">database</span><span class="o">.</span><span class="nf">getUserPasswordHash</span><span class="p">(</span><span class="nv">username</span><span class="p">:</span> <span class="n">username</span><span class="p">)</span> <span class="p">{</span>
            <span class="n">cacheService</span><span class="o">.</span><span class="nf">set</span><span class="p">(</span><span class="n">username</span><span class="p">,</span> <span class="nv">value</span><span class="p">:</span> <span class="n">passwordHash</span><span class="p">)</span>
            <span class="k">let</span> <span class="nv">success</span> <span class="o">=</span> <span class="n">passwordHash</span> <span class="o">==</span> <span class="n">password</span><span class="o">.</span><span class="nf">hashed</span><span class="p">()</span>
            <span class="n">securityHelper</span><span class="o">.</span><span class="nf">recordAuthenticationAttempt</span><span class="p">(</span><span class="nv">username</span><span class="p">:</span> <span class="n">username</span><span class="p">,</span> <span class="nv">success</span><span class="p">:</span> <span class="n">success</span><span class="p">)</span>
            <span class="n">notificationService</span><span class="o">.</span><span class="nf">sendAuthenticationEmail</span><span class="p">(</span><span class="nv">username</span><span class="p">:</span> <span class="n">username</span><span class="p">,</span> <span class="nv">success</span><span class="p">:</span> <span class="n">success</span><span class="p">)</span>
            <span class="k">return</span> <span class="n">success</span>
        <span class="p">}</span>
        
        <span class="n">loggerService</span><span class="o">.</span><span class="nf">log</span><span class="p">(</span><span class="s">"Authentication failed for user </span><span class="se">\(</span><span class="n">username</span><span class="se">)</span><span class="s">"</span><span class="p">)</span>
        <span class="n">securityHelper</span><span class="o">.</span><span class="nf">recordAuthenticationAttempt</span><span class="p">(</span><span class="nv">username</span><span class="p">:</span> <span class="n">username</span><span class="p">,</span> <span class="nv">success</span><span class="p">:</span> <span class="kc">false</span><span class="p">)</span>
        <span class="n">notificationService</span><span class="o">.</span><span class="nf">sendFailedAuthenticationAttemptEmail</span><span class="p">(</span><span class="nv">username</span><span class="p">:</span> <span class="n">username</span><span class="p">,</span> <span class="nv">success</span><span class="p">:</span> <span class="kc">false</span><span class="p">)</span>
        <span class="k">return</span> <span class="kc">false</span>
    <span class="p">}</span>
    
    <span class="kd">func</span> <span class="nf">register</span><span class="p">(</span><span class="nv">username</span><span class="p">:</span> <span class="kt">String</span><span class="p">,</span> <span class="nv">password</span><span class="p">:</span> <span class="kt">String</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">Bool</span> <span class="p">{</span>
        <span class="c1">// Check if the username is valid</span>
        <span class="k">guard</span> <span class="o">!</span><span class="n">username</span><span class="o">.</span><span class="n">isEmpty</span><span class="p">,</span> <span class="o">!</span><span class="n">password</span><span class="o">.</span><span class="n">isEmpty</span> <span class="k">else</span> <span class="p">{</span>
            <span class="n">loggerService</span><span class="o">.</span><span class="nf">log</span><span class="p">(</span><span class="s">"Registration failed: Username or password is empty."</span><span class="p">)</span>
            <span class="k">return</span> <span class="kc">false</span>
        <span class="p">}</span>
        
        <span class="c1">// Check if the username already exists in the database</span>
        <span class="k">if</span> <span class="n">database</span><span class="o">.</span><span class="nf">getUserPasswordHash</span><span class="p">(</span><span class="nv">username</span><span class="p">:</span> <span class="n">username</span><span class="p">)</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
            <span class="n">loggerService</span><span class="o">.</span><span class="nf">log</span><span class="p">(</span><span class="s">"Registration failed: Username </span><span class="se">\(</span><span class="n">username</span><span class="se">)</span><span class="s"> already exists."</span><span class="p">)</span>
            <span class="k">return</span> <span class="kc">false</span> <span class="c1">// Username already taken</span>
        <span class="p">}</span>
        
        <span class="c1">// Hash the password for storage</span>
        <span class="k">let</span> <span class="nv">passwordHash</span> <span class="o">=</span> <span class="n">password</span><span class="o">.</span><span class="nf">hashed</span><span class="p">()</span>
        
        <span class="c1">// Add the user to the database</span>
        <span class="n">database</span><span class="o">.</span><span class="nf">addUser</span><span class="p">(</span><span class="nv">username</span><span class="p">:</span> <span class="n">username</span><span class="p">,</span> <span class="nv">passwordHash</span><span class="p">:</span> <span class="n">passwordHash</span><span class="p">)</span>
        
        <span class="c1">// Optionally, cache the newly created user</span>
        <span class="n">cacheService</span><span class="o">.</span><span class="nf">set</span><span class="p">(</span><span class="n">username</span><span class="p">,</span> <span class="nv">value</span><span class="p">:</span> <span class="n">passwordHash</span><span class="p">)</span>
        
        <span class="c1">// Log the successful registration</span>
        <span class="n">loggerService</span><span class="o">.</span><span class="nf">log</span><span class="p">(</span><span class="s">"User </span><span class="se">\(</span><span class="n">username</span><span class="se">)</span><span class="s"> registered successfully."</span><span class="p">)</span>
        
        <span class="c1">// Send a notification email (optional)</span>
        <span class="n">notificationService</span><span class="o">.</span><span class="nf">sendRegistrationEmail</span><span class="p">(</span><span class="nv">username</span><span class="p">:</span> <span class="n">username</span><span class="p">,</span> <span class="nv">success</span><span class="p">:</span> <span class="kc">true</span><span class="p">)</span>
        
        <span class="k">return</span> <span class="kc">true</span>
    <span class="p">}</span>

<span class="p">}</span>
</code></pre></div></div>

<p>Below you can see the protocols used to create our dependencies, for example the <code class="language-plaintext highlighter-rouge">LoggerService</code> protocol can be used to either created a <code class="language-plaintext highlighter-rouge">LoggerServiceImpl</code> or <code class="language-plaintext highlighter-rouge">DummyLogger</code>, and the same goes for other dependencies, each protocol can be used to create either an implementation or a test double, this way we ensure the code is properly testable, we can simply plug in whatever needed in the <code class="language-plaintext highlighter-rouge">UserManager</code>.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// LoggerService.swift</span>
<span class="kd">protocol</span> <span class="kt">LoggerService</span> <span class="p">{</span>
    <span class="kd">func</span> <span class="nf">log</span><span class="p">(</span><span class="n">_</span> <span class="nv">message</span><span class="p">:</span> <span class="kt">String</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// CacheService.swift</span>
<span class="kd">protocol</span> <span class="kt">CacheService</span> <span class="p">{</span>
    <span class="kd">func</span> <span class="nf">get</span><span class="p">(</span><span class="n">_</span> <span class="nv">key</span><span class="p">:</span> <span class="kt">String</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">String</span><span class="p">?</span>
    <span class="kd">func</span> <span class="nf">set</span><span class="p">(</span><span class="n">_</span> <span class="nv">key</span><span class="p">:</span> <span class="kt">String</span><span class="p">,</span> <span class="nv">value</span><span class="p">:</span> <span class="kt">String</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// DatabaseHelper.swift</span>
<span class="kd">protocol</span> <span class="kt">DatabaseHelper</span> <span class="p">{</span>
    <span class="kd">func</span> <span class="nf">getUserPasswordHash</span><span class="p">(</span><span class="nv">username</span><span class="p">:</span> <span class="kt">String</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">String</span><span class="p">?</span>
    <span class="kd">func</span> <span class="nf">addUser</span><span class="p">(</span><span class="nv">username</span><span class="p">:</span> <span class="kt">String</span><span class="p">,</span> <span class="nv">passwordHash</span><span class="p">:</span> <span class="kt">String</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// SecurityHelper.swift</span>
<span class="kd">protocol</span> <span class="kt">SecurityHelper</span> <span class="p">{</span>
    <span class="kd">func</span> <span class="nf">recordAuthenticationAttempt</span><span class="p">(</span><span class="nv">username</span><span class="p">:</span> <span class="kt">String</span><span class="p">,</span> <span class="nv">success</span><span class="p">:</span> <span class="kt">Bool</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// NotificationService.swift</span>
<span class="kd">protocol</span> <span class="kt">NotificationService</span> <span class="p">{</span>
    <span class="kd">func</span> <span class="nf">sendAuthenticationEmail</span><span class="p">(</span><span class="nv">username</span><span class="p">:</span> <span class="kt">String</span><span class="p">,</span> <span class="nv">success</span><span class="p">:</span> <span class="kt">Bool</span><span class="p">)</span>
    <span class="kd">func</span> <span class="nf">sendRegistrationEmail</span><span class="p">(</span><span class="nv">username</span><span class="p">:</span> <span class="kt">String</span><span class="p">,</span> <span class="nv">success</span><span class="p">:</span> <span class="kt">Bool</span><span class="p">)</span>
    <span class="kd">func</span> <span class="nf">sendFailedAuthenticationAttemptEmail</span><span class="p">(</span><span class="nv">username</span><span class="p">:</span> <span class="kt">String</span><span class="p">,</span> <span class="nv">success</span><span class="p">:</span> <span class="kt">Bool</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Below is a simple hasher, this is simplified for demo purpose.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// String+Hasher.swift</span>
<span class="kd">extension</span> <span class="kt">String</span> <span class="p">{</span>
    <span class="kd">func</span> <span class="nf">hashed</span><span class="p">()</span> <span class="o">-&gt;</span> <span class="kt">String</span> <span class="p">{</span>
        <span class="k">return</span> <span class="s">"hashed_</span><span class="se">\(</span><span class="k">self</span><span class="se">)</span><span class="s">"</span> <span class="c1">// Simplified hash for demo purpose.</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Below you can find the implementations we will be using in production code, this is useful to compare when reading about the test double examples.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// LoggerServiceImpl.swift</span>
<span class="kd">class</span> <span class="kt">LoggerServiceImpl</span><span class="p">:</span> <span class="kt">LoggerService</span> <span class="p">{</span>
    <span class="kd">private</span> <span class="k">let</span> <span class="nv">logger</span> <span class="o">=</span> <span class="kt">Logger</span><span class="p">(</span><span class="nv">subsystem</span><span class="p">:</span> <span class="kt">Bundle</span><span class="o">.</span><span class="n">main</span><span class="o">.</span><span class="n">bundleIdentifier</span> <span class="p">??</span> <span class="s">"com.yourapp"</span><span class="p">,</span> <span class="nv">category</span><span class="p">:</span> <span class="s">"default"</span><span class="p">)</span>
    <span class="kd">func</span> <span class="nf">log</span><span class="p">(</span><span class="n">_</span> <span class="nv">message</span><span class="p">:</span> <span class="kt">String</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">logger</span><span class="o">.</span><span class="nf">log</span><span class="p">(</span><span class="s">"Log: </span><span class="se">\(</span><span class="n">message</span><span class="se">)</span><span class="s">"</span><span class="p">)</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// CacheServiceImpl.swift</span>
<span class="kd">class</span> <span class="kt">CacheServiceImpl</span><span class="p">:</span> <span class="kt">CacheService</span> <span class="p">{</span>
    <span class="kd">private</span> <span class="k">var</span> <span class="nv">cache</span><span class="p">:</span> <span class="p">[</span><span class="kt">String</span><span class="p">:</span> <span class="kt">String</span><span class="p">]</span> <span class="o">=</span> <span class="p">[:]</span>
    
    <span class="kd">func</span> <span class="nf">get</span><span class="p">(</span><span class="n">_</span> <span class="nv">key</span><span class="p">:</span> <span class="kt">String</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">String</span><span class="p">?</span> <span class="p">{</span>
        <span class="k">return</span> <span class="n">cache</span><span class="p">[</span><span class="n">key</span><span class="p">]</span> <span class="c1">// Retrieve value from cache</span>
    <span class="p">}</span>
    
    <span class="kd">func</span> <span class="nf">set</span><span class="p">(</span><span class="n">_</span> <span class="nv">key</span><span class="p">:</span> <span class="kt">String</span><span class="p">,</span> <span class="nv">value</span><span class="p">:</span> <span class="kt">String</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">cache</span><span class="p">[</span><span class="n">key</span><span class="p">]</span> <span class="o">=</span> <span class="n">value</span> <span class="c1">// Store value in cache</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// DatabaseHelperImpl.swift</span>
<span class="kd">class</span> <span class="kt">DatabaseHelperImpl</span><span class="p">:</span> <span class="kt">DatabaseHelper</span> <span class="p">{</span>
    <span class="kd">private</span> <span class="k">var</span> <span class="nv">users</span><span class="p">:</span> <span class="p">[</span><span class="kt">String</span><span class="p">:</span> <span class="kt">String</span><span class="p">]</span> <span class="o">=</span> <span class="p">[:]</span> <span class="c1">// Simulated user password storage</span>
    
    <span class="kd">func</span> <span class="nf">getUserPasswordHash</span><span class="p">(</span><span class="nv">username</span><span class="p">:</span> <span class="kt">String</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">String</span><span class="p">?</span> <span class="p">{</span>
        <span class="k">return</span> <span class="n">users</span><span class="p">[</span><span class="n">username</span><span class="p">]</span> <span class="c1">// Retrieve password hash for the given username</span>
    <span class="p">}</span>
    
    <span class="c1">// Helper method to add users to the database for testing purposes</span>
    <span class="kd">func</span> <span class="nf">addUser</span><span class="p">(</span><span class="nv">username</span><span class="p">:</span> <span class="kt">String</span><span class="p">,</span> <span class="nv">passwordHash</span><span class="p">:</span> <span class="kt">String</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">users</span><span class="p">[</span><span class="n">username</span><span class="p">]</span> <span class="o">=</span> <span class="n">passwordHash</span> <span class="c1">// Add user to the simulated database</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// SecurityHelperImpl.swift</span>
<span class="kd">class</span> <span class="kt">SecurityHelperImpl</span><span class="p">:</span> <span class="kt">SecurityHelper</span> <span class="p">{</span>
    <span class="kd">private</span> <span class="k">let</span> <span class="nv">logFileURL</span><span class="p">:</span> <span class="kt">URL</span>
    
    <span class="nf">init</span><span class="p">()</span> <span class="p">{</span>
        <span class="k">let</span> <span class="nv">documentsURL</span> <span class="o">=</span> <span class="kt">FileManager</span><span class="o">.</span><span class="k">default</span><span class="o">.</span><span class="nf">urls</span><span class="p">(</span><span class="nv">for</span><span class="p">:</span> <span class="o">.</span><span class="n">documentDirectory</span><span class="p">,</span> <span class="nv">in</span><span class="p">:</span> <span class="o">.</span><span class="n">userDomainMask</span><span class="p">)</span><span class="o">.</span><span class="n">first</span><span class="o">!</span>
        <span class="n">logFileURL</span> <span class="o">=</span> <span class="n">documentsURL</span><span class="o">.</span><span class="nf">appendingPathComponent</span><span class="p">(</span><span class="s">"authentication_log.txt"</span><span class="p">)</span>
    <span class="p">}</span>
    
    <span class="kd">func</span> <span class="nf">recordAuthenticationAttempt</span><span class="p">(</span><span class="nv">username</span><span class="p">:</span> <span class="kt">String</span><span class="p">,</span> <span class="nv">success</span><span class="p">:</span> <span class="kt">Bool</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">let</span> <span class="nv">logEntry</span> <span class="o">=</span> <span class="s">"</span><span class="se">\(</span><span class="kt">Date</span><span class="p">()</span><span class="se">)</span><span class="s">: Authentication attempt for </span><span class="se">\(</span><span class="n">username</span><span class="se">)</span><span class="s">: </span><span class="se">\(</span><span class="n">success</span> <span class="p">?</span> <span class="s">"Success"</span> <span class="p">:</span> <span class="s">"Failure"</span><span class="se">)\n</span><span class="s">"</span>
        
        <span class="k">do</span> <span class="p">{</span>
            <span class="k">try</span> <span class="n">logEntry</span><span class="o">.</span><span class="nf">write</span><span class="p">(</span><span class="nv">to</span><span class="p">:</span> <span class="n">logFileURL</span><span class="p">,</span> <span class="nv">atomically</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span> <span class="nv">encoding</span><span class="p">:</span> <span class="o">.</span><span class="n">utf8</span><span class="p">)</span>
        <span class="p">}</span> <span class="k">catch</span> <span class="p">{</span>
            <span class="nf">print</span><span class="p">(</span><span class="s">"Error writing to log file: </span><span class="se">\(</span><span class="n">error</span><span class="se">)</span><span class="s">"</span><span class="p">)</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="kd">func</span> <span class="nf">readAuthenticationAttempts</span><span class="p">()</span> <span class="o">-&gt;</span> <span class="p">[(</span><span class="nv">username</span><span class="p">:</span> <span class="kt">String</span><span class="p">,</span> <span class="nv">success</span><span class="p">:</span> <span class="kt">Bool</span><span class="p">)]</span> <span class="p">{</span>
        <span class="k">var</span> <span class="nv">attempts</span><span class="p">:</span> <span class="p">[(</span><span class="nv">username</span><span class="p">:</span> <span class="kt">String</span><span class="p">,</span> <span class="nv">success</span><span class="p">:</span> <span class="kt">Bool</span><span class="p">)]</span> <span class="o">=</span> <span class="p">[]</span>
        
        <span class="k">do</span> <span class="p">{</span>
            <span class="k">let</span> <span class="nv">logContents</span> <span class="o">=</span> <span class="k">try</span> <span class="kt">String</span><span class="p">(</span><span class="nv">contentsOf</span><span class="p">:</span> <span class="n">logFileURL</span><span class="p">,</span> <span class="nv">encoding</span><span class="p">:</span> <span class="o">.</span><span class="n">utf8</span><span class="p">)</span>
            <span class="k">let</span> <span class="nv">lines</span> <span class="o">=</span> <span class="n">logContents</span><span class="o">.</span><span class="nf">split</span><span class="p">(</span><span class="nv">separator</span><span class="p">:</span> <span class="s">"</span><span class="se">\n</span><span class="s">"</span><span class="p">)</span>
            
            <span class="k">for</span> <span class="n">line</span> <span class="k">in</span> <span class="n">lines</span> <span class="p">{</span>
                <span class="c1">// Parse each line to extract the username and success status</span>
                <span class="k">let</span> <span class="nv">components</span> <span class="o">=</span> <span class="n">line</span><span class="o">.</span><span class="nf">split</span><span class="p">(</span><span class="nv">separator</span><span class="p">:</span> <span class="s">":"</span><span class="p">)</span>
                <span class="k">if</span> <span class="n">components</span><span class="o">.</span><span class="n">count</span> <span class="o">&gt;=</span> <span class="mi">3</span> <span class="p">{</span>
                    <span class="k">let</span> <span class="nv">username</span> <span class="o">=</span> <span class="n">components</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span><span class="o">.</span><span class="nf">split</span><span class="p">(</span><span class="nv">separator</span><span class="p">:</span> <span class="s">" "</span><span class="p">)[</span><span class="mi">3</span><span class="p">]</span> <span class="c1">// Extract the username</span>
                    <span class="k">let</span> <span class="nv">successString</span> <span class="o">=</span> <span class="n">components</span><span class="p">[</span><span class="mi">3</span><span class="p">]</span><span class="o">.</span><span class="nf">trimmingCharacters</span><span class="p">(</span><span class="nv">in</span><span class="p">:</span> <span class="o">.</span><span class="n">whitespaces</span><span class="p">)</span> <span class="c1">// Extract "Success" or "Failure"</span>
                    <span class="k">let</span> <span class="nv">success</span> <span class="o">=</span> <span class="n">successString</span> <span class="o">==</span> <span class="s">"Success"</span>
                    <span class="n">attempts</span><span class="o">.</span><span class="nf">append</span><span class="p">((</span><span class="nv">username</span><span class="p">:</span> <span class="kt">String</span><span class="p">(</span><span class="n">username</span><span class="p">),</span> <span class="nv">success</span><span class="p">:</span> <span class="n">success</span><span class="p">))</span>
                <span class="p">}</span>
            <span class="p">}</span>
        <span class="p">}</span> <span class="k">catch</span> <span class="p">{</span>
            <span class="nf">print</span><span class="p">(</span><span class="s">"Error reading log file: </span><span class="se">\(</span><span class="n">error</span><span class="se">)</span><span class="s">"</span><span class="p">)</span>
        <span class="p">}</span>
        
        <span class="k">return</span> <span class="n">attempts</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// NotificationServiceImpl.swift</span>
<span class="kd">class</span> <span class="kt">NotificationServiceImpl</span><span class="p">:</span> <span class="kt">NotificationService</span> <span class="p">{</span>
    <span class="kd">func</span> <span class="nf">sendAuthenticationEmail</span><span class="p">(</span><span class="nv">username</span><span class="p">:</span> <span class="kt">String</span><span class="p">,</span> <span class="nv">success</span><span class="p">:</span> <span class="kt">Bool</span><span class="p">)</span> <span class="p">{</span>
        <span class="c1">// Here you could implement logic to send an email notification</span>
        <span class="nf">print</span><span class="p">(</span><span class="s">"Sent authentication email to </span><span class="se">\(</span><span class="n">username</span><span class="se">)</span><span class="s">: </span><span class="se">\(</span><span class="n">success</span> <span class="p">?</span> <span class="s">"Success"</span> <span class="p">:</span> <span class="s">"Failure"</span><span class="se">)</span><span class="s">"</span><span class="p">)</span>
    <span class="p">}</span>

    <span class="kd">func</span> <span class="nf">sendFailedAuthenticationAttemptEmail</span><span class="p">(</span><span class="nv">username</span><span class="p">:</span> <span class="kt">String</span><span class="p">,</span> <span class="nv">success</span><span class="p">:</span> <span class="kt">Bool</span><span class="p">)</span> <span class="p">{</span>
        <span class="c1">// Here you could implement logic to send an email notification for a failed attempt</span>
        <span class="nf">print</span><span class="p">(</span><span class="s">"Sent failed authentication attempt email to </span><span class="se">\(</span><span class="n">username</span><span class="se">)</span><span class="s">"</span><span class="p">)</span>
    <span class="p">}</span>
    
    <span class="kd">func</span> <span class="nf">sendRegistrationEmail</span><span class="p">(</span><span class="nv">username</span><span class="p">:</span> <span class="kt">String</span><span class="p">,</span> <span class="nv">success</span><span class="p">:</span> <span class="kt">Bool</span><span class="p">)</span> <span class="p">{</span>
        <span class="c1">// Here you could implement logic to send an email notification for registration</span>
        <span class="nf">print</span><span class="p">(</span><span class="s">"Sent registration email to </span><span class="se">\(</span><span class="n">username</span><span class="se">)</span><span class="s">"</span><span class="p">)</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>After getting familiar with <code class="language-plaintext highlighter-rouge">UserManager</code>, Let’s explore the different types of test doubles, along with examples and further details.</p>

<h2 id="1-dummies">1. Dummies</h2>

<p>A <strong>dummy</strong> test double is the simplest form of a test double. It’s used when a parameter is required by the method signature but isn’t actually used by the method itself. It’s essentially a placeholder to satisfy the API.</p>

<h3 id="example">Example:</h3>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// DummyLogger.swift</span>
<span class="kd">class</span> <span class="kt">DummyLogger</span><span class="p">:</span> <span class="kt">LoggerService</span> <span class="p">{</span>
    <span class="kd">func</span> <span class="nf">log</span><span class="p">(</span><span class="n">_</span> <span class="nv">message</span><span class="p">:</span> <span class="kt">String</span><span class="p">)</span> <span class="p">{</span>
        <span class="c1">// No-op: This dummy logger does nothing</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>In this example, <code class="language-plaintext highlighter-rouge">DummyLogger</code> is a <strong>dummy</strong> that satisfies the <code class="language-plaintext highlighter-rouge">LoggerService</code> protocol, but doesn’t actually perform any actions. It’s only used to fulfill the constructor requirements, notice that it has no <code class="language-plaintext highlighter-rouge">Logger</code>, in the unit tests cases, you can see the usage of that dummy, only to satisfy the needs we have.</p>

<p>If we had a complicated logging system that is supposed to be tested, we simply create another test double for the <code class="language-plaintext highlighter-rouge">LoggerService</code>, but again, we are trying to demonstrate the different types of test doubles, and the logger in this case seems to be the most simple dependency.</p>

<h2 id="2-fakes">2. Fakes</h2>

<p>A <strong>fake</strong> object provides a working implementation, but it is simpler and often less efficient than the real one. Fakes are often used to simulate databases or services during testing.</p>

<h3 id="example-1">Example:</h3>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="kt">FakeDatabase</span><span class="p">:</span> <span class="kt">DatabaseHelper</span> <span class="p">{</span>
    <span class="k">var</span> <span class="nv">users</span><span class="p">:</span> <span class="p">[</span><span class="kt">String</span><span class="p">:</span> <span class="kt">String</span><span class="p">]</span> <span class="o">=</span> <span class="p">[:]</span> <span class="c1">// Simulated user password storage</span>
    
    <span class="kd">func</span> <span class="nf">getUserPasswordHash</span><span class="p">(</span><span class="nv">username</span><span class="p">:</span> <span class="kt">String</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">String</span><span class="p">?</span> <span class="p">{</span>
        <span class="k">return</span> <span class="n">users</span><span class="p">[</span><span class="n">username</span><span class="p">]</span>
    <span class="p">}</span>
    
    <span class="c1">// Helper method to add users to the fake database</span>
    <span class="kd">func</span> <span class="nf">addUser</span><span class="p">(</span><span class="nv">username</span><span class="p">:</span> <span class="kt">String</span><span class="p">,</span> <span class="nv">passwordHash</span><span class="p">:</span> <span class="kt">String</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">users</span><span class="p">[</span><span class="n">username</span><span class="p">]</span> <span class="o">=</span> <span class="n">passwordHash</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Here, the <code class="language-plaintext highlighter-rouge">FakeDatabase</code> simulates a database with an in-memory data structure, making it useful for testing without involving a real database. The original implementation is similar due to the demo-purpose, we can have the original implementation to really talk to a simple sqlite database or a coredata database.</p>

<p>The fake test double only does a dictionary manipulation, using an original database example will make this blog post very lengthy, in the next example we can see a usage of user defaults.</p>

<h2 id="3-stubs">3. Stubs</h2>

<p>A <strong>stub</strong> is a test double that provides predefined answers to method calls. Stubs don’t perform any logic; they just return canned responses. Stubs are helpful when you want to control the return values of a dependency in a test scenario.</p>

<h3 id="example-2">Example:</h3>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// StubCache.swift</span>
<span class="kd">class</span> <span class="kt">StubCache</span><span class="p">:</span> <span class="kt">CacheService</span> <span class="p">{</span>
    <span class="k">var</span> <span class="nv">storedData</span><span class="p">:</span> <span class="p">[</span><span class="kt">String</span><span class="p">:</span> <span class="kt">String</span><span class="p">]</span> <span class="o">=</span> <span class="p">[:]</span>
    
    <span class="kd">func</span> <span class="nf">get</span><span class="p">(</span><span class="n">_</span> <span class="nv">key</span><span class="p">:</span> <span class="kt">String</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">String</span><span class="p">?</span> <span class="p">{</span>
        <span class="k">return</span> <span class="n">storedData</span><span class="p">[</span><span class="n">key</span><span class="p">]</span> <span class="c1">// Simulates cache hit or miss</span>
    <span class="p">}</span>
    
    <span class="kd">func</span> <span class="nf">set</span><span class="p">(</span><span class="n">_</span> <span class="nv">key</span><span class="p">:</span> <span class="kt">String</span><span class="p">,</span> <span class="nv">value</span><span class="p">:</span> <span class="kt">String</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">storedData</span><span class="p">[</span><span class="n">key</span><span class="p">]</span> <span class="o">=</span> <span class="n">value</span> <span class="c1">// Simulates setting a value in the cache</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>If we compare with the original implementation, stubs use a dictionary to store data, instead of using <code class="language-plaintext highlighter-rouge">UserDefaults</code>.</p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// CacheServiceImpl.swift</span>
<span class="kd">class</span> <span class="kt">CacheServiceImpl</span><span class="p">:</span> <span class="kt">CacheService</span> <span class="p">{</span>
    <span class="kd">private</span> <span class="k">let</span> <span class="nv">userDefaults</span> <span class="o">=</span> <span class="kt">UserDefaults</span><span class="o">.</span><span class="n">standard</span>
    
    <span class="kd">func</span> <span class="nf">get</span><span class="p">(</span><span class="n">_</span> <span class="nv">key</span><span class="p">:</span> <span class="kt">String</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">String</span><span class="p">?</span> <span class="p">{</span>
        <span class="k">return</span> <span class="n">userDefaults</span><span class="o">.</span><span class="nf">string</span><span class="p">(</span><span class="nv">forKey</span><span class="p">:</span> <span class="n">key</span><span class="p">)</span> <span class="c1">// Retrieve value from UserDefaults</span>
    <span class="p">}</span>
    
    <span class="kd">func</span> <span class="nf">set</span><span class="p">(</span><span class="n">_</span> <span class="nv">key</span><span class="p">:</span> <span class="kt">String</span><span class="p">,</span> <span class="nv">value</span><span class="p">:</span> <span class="kt">String</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">userDefaults</span><span class="o">.</span><span class="nf">set</span><span class="p">(</span><span class="n">value</span><span class="p">,</span> <span class="nv">forKey</span><span class="p">:</span> <span class="n">key</span><span class="p">)</span> <span class="c1">// Store value in UserDefaults</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<blockquote>
  <p>Note: Stubs provide canned responses to method calls, while fakes provide a simplified implementation of the real thing.</p>
</blockquote>

<blockquote>
  <p>Note: the usage of UserDefaults should not be used with critical data storage like passwords, even if hashed, this is only for demo purpose.</p>
</blockquote>

<h2 id="4-spies">4. Spies</h2>

<p>A <strong>spy</strong> is a test double that records information about the interactions with its methods, such as how many times a method was called or with what arguments. This makes it useful for verifying side effects in your tests.</p>

<h3 id="example-3">Example:</h3>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// SpySecurityHelper.swift</span>
<span class="kd">class</span> <span class="kt">SpySecurityHelper</span><span class="p">:</span> <span class="kt">SecurityHelper</span> <span class="p">{</span>
    <span class="k">var</span> <span class="nv">recordedAttempts</span><span class="p">:</span> <span class="p">[(</span><span class="nv">username</span><span class="p">:</span> <span class="kt">String</span><span class="p">,</span> <span class="nv">success</span><span class="p">:</span> <span class="kt">Bool</span><span class="p">)]</span> <span class="o">=</span> <span class="p">[]</span>
    
    <span class="kd">func</span> <span class="nf">recordAuthenticationAttempt</span><span class="p">(</span><span class="nv">username</span><span class="p">:</span> <span class="kt">String</span><span class="p">,</span> <span class="nv">success</span><span class="p">:</span> <span class="kt">Bool</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">recordedAttempts</span><span class="o">.</span><span class="nf">append</span><span class="p">((</span><span class="n">username</span><span class="p">,</span> <span class="n">success</span><span class="p">))</span>
    <span class="p">}</span>
    
    <span class="c1">// Helper method to check whether a specific attempt was recorded</span>
    <span class="kd">func</span> <span class="nf">verifyAttempt</span><span class="p">(</span><span class="nv">username</span><span class="p">:</span> <span class="kt">String</span><span class="p">,</span> <span class="nv">success</span><span class="p">:</span> <span class="kt">Bool</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">Bool</span> <span class="p">{</span>
        <span class="k">return</span> <span class="n">recordedAttempts</span><span class="o">.</span><span class="n">contains</span> <span class="p">{</span> <span class="nv">$0</span><span class="o">.</span><span class="n">username</span> <span class="o">==</span> <span class="n">username</span> <span class="o">&amp;&amp;</span> <span class="nv">$0</span><span class="o">.</span><span class="n">success</span> <span class="o">==</span> <span class="n">success</span> <span class="p">}</span>
    <span class="p">}</span>
    
    <span class="c1">// Helper method to check the total number of attempts</span>
    <span class="kd">func</span> <span class="nf">verifyTotalAttempts</span><span class="p">(</span><span class="nv">expectedCount</span><span class="p">:</span> <span class="kt">Int</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">Bool</span> <span class="p">{</span>
        <span class="k">return</span> <span class="n">recordedAttempts</span><span class="o">.</span><span class="n">count</span> <span class="o">==</span> <span class="n">expectedCount</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>We can see that the <code class="language-plaintext highlighter-rouge">SpySecurityHelper</code> is more simple than the original implementation, the spy test double records all attempts of login, and checks the side effects of the dependency.</p>

<h2 id="5-mocks">5. Mocks</h2>

<p>A <strong>mock</strong> is the most sophisticated type of test double. It allows you to set expectations about interactions with the object and verify that those expectations are met. Mocks are often used in combination with testing frameworks.</p>

<h3 id="example-4">Example:</h3>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// MockNotificationService.swift</span>
<span class="kd">class</span> <span class="kt">MockNotificationService</span><span class="p">:</span> <span class="kt">NotificationService</span> <span class="p">{</span>
    <span class="k">var</span> <span class="nv">sentEmails</span><span class="p">:</span> <span class="p">[(</span><span class="nv">username</span><span class="p">:</span> <span class="kt">String</span><span class="p">,</span> <span class="nv">success</span><span class="p">:</span> <span class="kt">Bool</span><span class="p">)]</span> <span class="o">=</span> <span class="p">[]</span>
    
    <span class="kd">func</span> <span class="nf">sendRegistrationEmail</span><span class="p">(</span><span class="nv">username</span><span class="p">:</span> <span class="kt">String</span><span class="p">,</span> <span class="nv">success</span><span class="p">:</span> <span class="kt">Bool</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">sentEmails</span><span class="o">.</span><span class="nf">append</span><span class="p">((</span><span class="n">username</span><span class="p">,</span> <span class="n">success</span><span class="p">))</span>
    <span class="p">}</span>

    <span class="kd">func</span> <span class="nf">sendAuthenticationEmail</span><span class="p">(</span><span class="nv">username</span><span class="p">:</span> <span class="kt">String</span><span class="p">,</span> <span class="nv">success</span><span class="p">:</span> <span class="kt">Bool</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">sentEmails</span><span class="o">.</span><span class="nf">append</span><span class="p">((</span><span class="n">username</span><span class="p">,</span> <span class="n">success</span><span class="p">))</span>
    <span class="p">}</span>

    <span class="kd">func</span> <span class="nf">sendFailedAuthenticationAttemptEmail</span><span class="p">(</span><span class="nv">username</span><span class="p">:</span> <span class="kt">String</span><span class="p">,</span> <span class="nv">success</span><span class="p">:</span> <span class="kt">Bool</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">sentEmails</span><span class="o">.</span><span class="nf">append</span><span class="p">((</span><span class="n">username</span><span class="p">,</span> <span class="n">success</span><span class="p">))</span>
    <span class="p">}</span>

    <span class="c1">// Helper to verify that an email was sent</span>
    <span class="kd">func</span> <span class="nf">verifyEmailSent</span><span class="p">(</span><span class="n">to</span> <span class="nv">username</span><span class="p">:</span> <span class="kt">String</span><span class="p">,</span> <span class="nv">success</span><span class="p">:</span> <span class="kt">Bool</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">Bool</span> <span class="p">{</span>
        <span class="k">return</span> <span class="n">sentEmails</span><span class="o">.</span><span class="n">contains</span> <span class="p">{</span> <span class="nv">$0</span><span class="o">.</span><span class="n">username</span> <span class="o">==</span> <span class="n">username</span> <span class="o">&amp;&amp;</span> <span class="nv">$0</span><span class="o">.</span><span class="n">success</span> <span class="o">==</span> <span class="n">success</span> <span class="p">}</span>
    <span class="p">}</span>
    
    <span class="c1">// Helper to verify the total number of emails sent</span>
    <span class="kd">func</span> <span class="nf">verifyTotalEmailsSent</span><span class="p">(</span><span class="nv">expectedCount</span><span class="p">:</span> <span class="kt">Int</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="kt">Bool</span> <span class="p">{</span>
        <span class="k">return</span> <span class="n">sentEmails</span><span class="o">.</span><span class="n">count</span> <span class="o">==</span> <span class="n">expectedCount</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<h2 id="sample-code">Sample Code</h2>

<p>It’s best to use a real project to test our the code, you can download the source code <a href="/assets/test_doubles.zip">Here</a>.</p>

<!-- _includes/centered-image.html -->
<div class="centered-image">
  <img src="/images/posts/auth_screen.webp" alt="" class="centered-image__img" />
  <p align="center">
    <em>Simple SwiftUI Screen</em>
  </p>
</div>

<h2 id="conclusion">Conclusion</h2>

<p>Test doubles are a powerful concept that can significantly improve the reliability and maintainability of your tests. Whether you’re using dummies, fakes, stubs, spies, or mocks, each type of test double serves a unique purpose in ensuring your code is thoroughly tested in isolation.</p>

<p>By understanding when and how to use each type, you’ll be able to write more focused and effective unit tests in Swift, ensuring your code is both high quality and easier to maintain.</p>

<p><strong>Happy testing!</strong> 🎉</p>]]></content><author><name>Deya Elkhawaldeh</name></author><category term="Development" /><category term="iOS" /><category term="Programming" /><category term="Swift" /><category term="Development" /><category term="iOS" /><category term="Programming" /><category term="Swift" /><summary type="html"><![CDATA[The term test doubles draws inspiration from stunt doubles in the movie industry, where a stunt double steps in to perform dangerous or complex tasks, allowing the actor to focus on their role. Similarly, in software testing, test doubles step in to replace real components, making testing simpler, faster, and more reliable.]]></summary></entry><entry><title type="html">iOS Accessibility Basics</title><link href="https://swiftbydeya.com/ios-accessibility/" rel="alternate" type="text/html" title="iOS Accessibility Basics" /><published>2023-08-02T00:00:00+03:00</published><updated>2024-09-08T00:00:00+03:00</updated><id>https://swiftbydeya.com/ios-accessibility-series-part-basics</id><content type="html" xml:base="https://swiftbydeya.com/ios-accessibility/"><![CDATA[<p>iOS accessibility is a vital aspect of app development that focuses on creating applications that are usable and inclusive for individuals with disabilities. This encompasses a comprehensive set of tools, technologies, and guidelines <a href="https://developer.apple.com/accessibility/">provided by Apple</a>, designed to empower developers to build apps that cater to a diverse range of users.</p>

<!--more-->

<p>In today’s digital landscape, where mobile applications play a crucial role in daily life, ensuring accessibility is not just a legal requirement but also a moral imperative. By integrating accessibility features from the outset, developers can create a more equitable experience for all users, including those with visual, auditory, cognitive, or physical impairments.</p>

<p>Accessibility should never be an afterthought; rather, it should be woven into the fabric of app design and development. This proactive approach not only enhances the usability of applications but also broadens the potential user base, fostering a more inclusive digital environment. By prioritizing accessibility, developers can contribute to a world where technology is accessible to everyone, regardless of their abilities.</p>

<!-- _includes/centered-image.html -->
<div class="centered-image">
  <img src="../images/covers/a11y_full.webp" alt="" class="centered-image__img" width="960" height="1568" />
  <p align="center">
    <em></em>
  </p>
</div>

<h2 id="why-accessibility-matters">Why Accessibility Matters</h2>

<p>Making your app accessible broadens your user base by including people with diverse needs such as visual, auditory, motor, and cognitive impairments. In addition to the social and ethical aspects, accessibility can positively affect your app’s user experience, engagement, and retention.</p>

<h3 id="common-misconceptions-about-accessibility">Common Misconceptions about Accessibility</h3>

<p>Many developers and businesses overlook accessibility for several reasons:</p>
<ol>
  <li><strong>Lack of awareness</strong>: Developers may not realize the significance and scope of accessibility.</li>
  <li><strong>Perceived complexity</strong>: Implementing accessibility is often seen as difficult or time-consuming.</li>
  <li><strong>Misaligned priorities</strong>: Businesses prioritize visual aesthetics or other features over accessibility.</li>
  <li><strong>Costs and resources</strong>: There’s a belief that making apps accessible requires significant time, money, and resources.</li>
  <li><strong>Assumed target audience</strong>: Developers sometimes think their audience does not include people with disabilities, which is rarely accurate.</li>
</ol>

<p>Despite these challenges, investing in accessibility not only benefits users with disabilities but also enhances the overall user experience for everyone. Let’s explore how accessibility laws and regulations play a role in encouraging more inclusive apps.</p>

<h2 id="legal-implications-of-non-compliance">Legal Implications of Non-Compliance</h2>

<p>Several countries enforce digital accessibility through laws and regulations. In the United States, the <strong>Americans with Disabilities Act (ADA)</strong> and <strong>Section 508</strong> of the Rehabilitation Act mandate accessibility for digital content offered by federal agencies or organizations that receive federal funding.</p>

<h3 id="global-accessibility-standards">Global Accessibility Standards</h3>

<p>The <strong>Web Content Accessibility Guidelines (WCAG)</strong>, developed by the <strong>World Wide Web Consortium (W3C)</strong> and the <strong>Web Accessibility Initiative (WAI)</strong>, are internationally recognized as the primary standard for ensuring digital accessibility. Adopting WCAG principles in your iOS app ensures it meets the needs of users with disabilities while avoiding legal risks.</p>

<h3 id="legal-consequences-for-non-compliance">Legal Consequences for Non-Compliance</h3>

<p>Failure to comply with accessibility regulations can lead to:</p>
<ul>
  <li><strong>Lawsuits</strong>: Companies may face legal action for failing to provide accessible digital experiences.</li>
  <li><strong>Fines and penalties</strong>: Non-compliance can result in hefty fines and settlement costs.</li>
  <li><strong>Reputational damage</strong>: Poor accessibility can harm a company’s image and alienate potential customers.</li>
</ul>

<p>Several high-profile cases, such as lawsuits against Domino’s Pizza, Netflix, and Target, have set important legal precedents. These cases highlight the need for accessibility in both websites and mobile applications, with courts ruling in favor of plaintiffs seeking more inclusive experiences.</p>

<h2 id="ios-accessibility-tools-and-features">iOS Accessibility Tools and Features</h2>

<p>As an iOS developer, Apple provides various tools and APIs to incorporate accessibility features in your apps.</p>

<h3 id="voiceover">VoiceOver</h3>

<p><strong>VoiceOver</strong> is a built-in screen reader that allows users with visual impairments to interact with their devices using spoken feedback. It’s essential to ensure that your app supports VoiceOver by providing meaningful labels for UI elements and enabling users to navigate the app through gestures.</p>

<h3 id="dynamic-text">Dynamic Text</h3>

<p>Dynamic text allows users to adjust the size of the text to suit their reading preferences. To support this, make sure your app uses <code class="language-plaintext highlighter-rouge">UIFontMetrics</code> and resizable fonts so that text scales correctly across the interface.</p>

<h3 id="switch-control-and-assistivetouch">Switch Control and AssistiveTouch</h3>

<p><strong>Switch Control</strong> and <strong>AssistiveTouch</strong> help users with motor impairments to interact with their devices using alternate input methods. Developers can improve accessibility by ensuring that touch targets are large and well-spaced, and by supporting these alternative input methods.</p>

<h3 id="closed-captions-and-subtitles">Closed Captions and Subtitles</h3>

<p>For users with auditory impairments, closed captions and subtitles are crucial. If your app includes audio or video content, providing closed captioning is essential to ensure accessibility.</p>

<h2 id="types-of-disabilities-and-how-to-accommodate-them">Types of Disabilities and How to Accommodate Them</h2>

<p>It’s crucial to understand the diverse range of impairments and how each can affect the user experience. Here’s a breakdown:</p>

<table>
  <thead>
    <tr>
      <th>Disability Type</th>
      <th>Accessibility Considerations</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><strong>Visual</strong></td>
      <td>Users with visual impairments may rely on screen readers like VoiceOver or require high contrast modes. Ensure your app provides meaningful labels, supports dynamic text, and avoids color-only cues. Support users with conditions such as blindness, low vision, and color blindness.</td>
    </tr>
    <tr>
      <td><strong>Auditory</strong></td>
      <td>Users with hearing impairments depend on visual alternatives like subtitles, closed captions, and visual cues for alerts. Consider providing haptic feedback for notifications.</td>
    </tr>
    <tr>
      <td><strong>Motor</strong></td>
      <td>Users with limited dexterity benefit from larger touch targets and alternative input methods like Switch Control or voice commands. Simplifying interactions and allowing customizable gestures enhance usability.</td>
    </tr>
    <tr>
      <td><strong>Cognitive</strong></td>
      <td>Users with cognitive impairments may face challenges with memory, attention, or problem-solving. Clear instructions, simplified navigation, and minimal distractions improve the experience for these users.</td>
    </tr>
    <tr>
      <td><strong>Speech</strong></td>
      <td>Users with speech impairments might require alternative input methods like text-to-speech or communication aids. Consider incorporating Augmentative and Alternative Communication (AAC) tools.</td>
    </tr>
    <tr>
      <td><strong>Situational</strong></td>
      <td>Situational impairments, such as using an app in a noisy environment or under bright light, can benefit from features like closed captioning, adjustable brightness, and larger text sizes.</td>
    </tr>
  </tbody>
</table>

<h2 id="accessibility-best-practices">Accessibility Best Practices</h2>

<p>To ensure your app meets accessibility standards, follow these best practices:</p>
<ul>
  <li><strong>Label UI Elements</strong>: Provide meaningful, descriptive labels for all interactive elements.</li>
  <li><strong>Test with Assistive Technologies</strong>: Regularly test your app using tools like VoiceOver and Switch Control.</li>
  <li><strong>Support Dynamic Type</strong>: Ensure your app adapts to various text sizes.</li>
  <li><strong>Use Semantics</strong>: Make sure buttons, links, and other interactive elements have proper roles in accessibility APIs.</li>
  <li><strong>Color and Contrast</strong>: Avoid relying solely on color to convey information, and maintain sufficient contrast for readability.</li>
</ul>

<h2 id="conclusion">Conclusion</h2>

<p>Prioritizing accessibility in your iOS app development process not only ensures compliance with laws and standards but also creates a more inclusive user experience. By incorporating these best practices and leveraging Apple’s powerful accessibility tools, you can build applications that are usable by everyone, regardless of their abilities.</p>

<p>Accessibility is not just a feature—it’s a responsibility that every developer should embrace to ensure technology empowers all users.</p>]]></content><author><name>Deya Elkhawaldeh</name></author><category term="Development" /><category term="iOS" /><category term="Programming" /><category term="Swift" /><category term="Development" /><category term="iOS" /><category term="Programming" /><category term="Swift" /><summary type="html"><![CDATA[iOS accessibility is a vital aspect of app development that focuses on creating applications that are usable and inclusive for individuals with disabilities. This encompasses a comprehensive set of tools, technologies, and guidelines provided by Apple, designed to empower developers to build apps that cater to a diverse range of users.]]></summary></entry><entry><title type="html">SwiftUI Views Are Values, Not Objects: Understanding the Implications</title><link href="https://swiftbydeya.com/swiftui-views-are-values-and-not-objects-overlooking-this-can-lead-to-bugs/" rel="alternate" type="text/html" title="SwiftUI Views Are Values, Not Objects: Understanding the Implications" /><published>2023-05-19T00:00:00+03:00</published><updated>2024-09-08T00:00:00+03:00</updated><id>https://swiftbydeya.com/swiftui-views-are-values-and-not-objects-overlooking-this-can-lead-to-bugs</id><content type="html" xml:base="https://swiftbydeya.com/swiftui-views-are-values-and-not-objects-overlooking-this-can-lead-to-bugs/"><![CDATA[<p>In SwiftUI, views are fundamentally designed as value types rather than traditional objects. This design approach is a key aspect of SwiftUI’s declarative programming model and aligns with the Swift language’s emphasis on value semantics.</p>

<!--more-->

<p>By treating views as values, SwiftUI promotes predictable behavior and enables developers to manage state and data flow more effectively. This paradigm shift towards a more functional and declarative style of programming empowers developers to describe the desired state of the user interface, while SwiftUI handles the complexities of managing the view hierarchy. Understanding this distinction is crucial for developers to avoid common pitfalls that can lead to bugs in their applications and ensure a smooth and efficient user experience. 🧐</p>

<!-- _includes/centered-image.html -->
<div class="centered-image">
  <img src="../images/covers/swiftui_inside_full.webp" alt="" class="centered-image__img" width="960" height="1568" />
  <p align="center">
    <em></em>
  </p>
</div>

<h2 id="the-nature-of-value-types-in-swiftui">The Nature of Value Types in SwiftUI</h2>

<p>As value types, views in SwiftUI are immutable. When you modify a view, you are actually creating a new instance with the desired changes rather than mutating the existing view. This immutability allows SwiftUI to efficiently track changes and perform targeted updates to the user interface. When state changes, SwiftUI can determine the minimal set of updates required to reflect the new state, enhancing performance and responsiveness.</p>

<h3 id="benefits-of-value-types">Benefits of Value Types</h3>

<ol>
  <li>
    <p><strong>Predictable Behavior</strong>: Since views are copied when needed, developers can expect consistent behavior, making it easier to manage state and data flow.</p>
  </li>
  <li>
    <p><strong>Functional Programming Paradigm</strong>: Value types promote a more functional and declarative style of programming, allowing developers to describe the desired state while SwiftUI manages the view hierarchy.</p>
  </li>
  <li>
    <p><strong>Thread Safety</strong>: Value types are inherently thread-safe, as copies are made when passing views between different execution contexts, reducing the risk of concurrency issues.</p>
  </li>
  <li>
    <p><strong>Built-in Animations</strong>: SwiftUI can automatically animate changes by comparing old and new values of a view, resulting in smooth and visually appealing user interface updates.</p>
  </li>
</ol>

<h2 id="declarative-programming-paradigm">Declarative Programming Paradigm</h2>

<p>The declarative programming paradigm is central to SwiftUI’s design philosophy. Instead of specifying step-by-step instructions on how to achieve a particular state, developers describe the desired state of the user interface. SwiftUI’s view tree engine leverages this approach to efficiently manage and update the user interface based on changes in the underlying state.</p>

<h3 id="composable-view-hierarchy">Composable View Hierarchy</h3>

<p>In SwiftUI, you define your user interface using a hierarchy of composable and reusable views. Each view represents a specific part of the interface and is responsible for rendering itself based on the current state. This tree-like structure, known as the view hierarchy, is immutable. When the state changes, SwiftUI re-evaluates the view hierarchy and determines the necessary updates, a process known as the reconciliation algorithm (or diffing algorithm).</p>

<h2 id="the-reconciliation-algorithm">The Reconciliation Algorithm</h2>

<p>The reconciliation algorithm is where SwiftUI’s view tree engine excels. It efficiently compares the old and new view hierarchies, identifies differences, and applies the necessary updates to the user interface. By only updating the specific parts of the view hierarchy that have changed, SwiftUI minimizes the workload needed to keep the UI in sync with the state, resulting in optimal performance.</p>

<h3 id="comparison-with-uikit">Comparison with UIKit</h3>

<p>In contrast to UIKit, SwiftUI unifies view construction and updates into a single code path. Views are values rather than objects, described by values conforming to the View protocol. The view tree is transient and can be recreated at any time based on the underlying state. This declarative approach eliminates the need for separate event handlers and manual UI updates, as seen in UIKit. SwiftUI’s view tree engine efficiently reconciles state changes, performs targeted updates, and ensures a reactive UI that stays in sync with the data.</p>

<p>By relying on value semantics, SwiftUI can perform granular updates and avoid unnecessary computations, leading to a highly performant and responsive user interface.</p>

<h2 id="embracing-a-declarative-mindset">Embracing a Declarative Mindset</h2>

<p>By adopting a declarative approach, SwiftUI allows developers to focus on describing the desired end state of the UI rather than worrying about the low-level details of UI manipulation. This shift in mindset leads to more maintainable and expressive code, as developers can easily reason about the UI based on its desired state.</p>

<h3 id="reactive-programming-model">Reactive Programming Model</h3>

<p>Another significant aspect of SwiftUI’s view tree engine is its ability to efficiently handle updates. As views in SwiftUI are value types, changes in the state result in the creation of new view instances rather than mutating existing ones. SwiftUI employs a mechanism called “value comparison” to determine the differences between the old and new views, enabling it to perform targeted updates to the UI.</p>

<p>Additionally, SwiftUI’s view tree engine embraces a reactive programming model. Views in SwiftUI are not just passive representations of UI elements; they react to changes in the state. This reactive nature ensures that the UI remains synchronized with the underlying data, providing a seamless user experience.</p>

<h2 id="conclusion">Conclusion</h2>

<p>In conclusion, SwiftUI’s view tree engine revolutionizes UI development by embracing the declarative programming paradigm. By leveraging value types, value comparisons, and reactive programming, SwiftUI provides an efficient and responsive user interface. The ability to describe UI in a declarative manner, combined with targeted updates and optimization techniques, empowers developers to create intuitive and performant user interfaces with ease. Understanding the value semantics of SwiftUI views is essential for building robust applications and avoiding common pitfalls that can arise from overlooking this fundamental aspect of the framework.</p>]]></content><author><name>Deya Elkhawaldeh</name></author><category term="Development" /><category term="iOS" /><category term="Programming" /><category term="Swift" /><category term="Development" /><category term="iOS" /><category term="Programming" /><category term="Swift" /><summary type="html"><![CDATA[In SwiftUI, views are fundamentally designed as value types rather than traditional objects. This design approach is a key aspect of SwiftUI’s declarative programming model and aligns with the Swift language’s emphasis on value semantics.]]></summary></entry><entry><title type="html">This Is Why I Don’t Prefer Git GUI Tools</title><link href="https://swiftbydeya.com/this-is-why-i-dont-use-git-gui-tools/" rel="alternate" type="text/html" title="This Is Why I Don’t Prefer Git GUI Tools" /><published>2023-01-30T00:00:00+03:00</published><updated>2024-09-08T00:00:00+03:00</updated><id>https://swiftbydeya.com/this-is-why-i-dont-use-git-gui-tools</id><content type="html" xml:base="https://swiftbydeya.com/this-is-why-i-dont-use-git-gui-tools/"><![CDATA[<p>Almost anyone who uses Xcode can quickly notice that it lacks many essential Git features, which is acceptable in some way since it’s primarily a development environment rather than a dedicated source control application.</p>

<!--more-->

<p>The features available in Xcode may suffice for personal or small projects, but when working within a larger team, relying solely on the IDE can lead to significant challenges in managing source control effectively.</p>

<!-- _includes/centered-image.html -->
<div class="centered-image">
  <img src="../images/covers/git_cli_full.webp" alt="" class="centered-image__img" width="960" height="1568" />
  <p align="center">
    <em></em>
  </p>
</div>

<!-- _includes/centered-image.html -->
<div class="centered-image">
  <img src="/images/posts/image-3.webp" alt="" class="centered-image__img" />
  <p align="center">
    <em>Xcode's Source Control</em>
  </p>
</div>

<p>Many engineers have also observed that file status markers like “A” (added), “M” (modified), and “C” (conflicted) often remain stuck, making it difficult to determine the current state of files at a glance.</p>

<p>Convincing backend engineers to use the terminal is relatively straightforward, as GUI tools do not automatically update the repository’s trunk on a server daily at 1:30 AM. However, a cron job that utilizes the CLI can easily perform this task. On the other hand, persuading mobile developers may be more challenging since the need for terminal usage is not always immediately apparent.</p>

<h2 id="real-life-examples-of-using-the-git-cli">Real-Life Examples of Using the Git CLI</h2>

<p>Let’s explore a few real-life examples that highlight the advantages of using the Git CLI over GUI tools. While I could list numerous cases, I will keep this post concise by focusing on a couple of key commands that illustrate the power of the terminal.</p>

<h3 id="1-pr-reverts">1. PR Reverts</h3>

<p>GitHub introduced a feature for reverting pull requests (PRs). If you have an already merged PR and need to revert it before a tight deadline, using GitHub’s built-in revert feature does not provide details about which commits will be removed. This can lead to accidentally removing unrelated commits.</p>

<p>By contrast, using the command:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git revert <span class="nt">--no-commit</span> someHash
</code></pre></div></div>

<p>gives you fine-grained control over which commits to remove or retain, allowing for a more precise and safe rollback.</p>

<h3 id="2-submodules">2. Submodules</h3>

<p>When dealing with nested Git repositories (submodules), many GUI tools lack robust support for managing these structures. The Git CLI, however, provides comprehensive commands to add, update, and manage submodules effectively.</p>

<p>I typically keep the terminal open throughout the day; I can’t imagine working without it. I prefer understanding exactly what each command does rather than relying on button clicks in a GUI. While tools may come and go, the CLI remains the foundation upon which GUI tools are built.</p>

<h2 id="the-benefits-of-using-the-git-cli">The Benefits of Using the Git CLI</h2>

<p>Using the command line allows for the convenient setup of install scripts, build scripts, deployment scripts, and more. In a large team setting, it is often unclear what exactly happens within a GUI-based application. I’ve witnessed colleagues make irreversible mistakes that could only be rectified using the CLI.</p>

<p>I find that I am several times more productive using the command line compared to navigating through menus with a mouse. While Git GUI tools aim to simplify the process, they can inadvertently add complexity, especially in larger projects. I’ve encountered non-standard terminology in some GUI tools that can make understanding Git more difficult.</p>

<p>Xcode comes with a diffing tool, which visualize differences effectively. However, for serious Git users, mastering the command line is crucial. It provides the flexibility, control, and efficiency needed to manage complex projects and collaborate effectively with team members.</p>

<h2 id="common-git-commands">Common Git Commands</h2>

<p>Here is a table of common Git commands that I frequently use, sorted alphabetically, along with their descriptions:</p>

<table>
  <thead>
    <tr>
      <th>Command</th>
      <th>Description</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">git add</code></td>
      <td>Stages changes in your working directory for the next commit.</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">git annotate</code></td>
      <td>Displays the last modification of each line in a file, showing who made the change and when.</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">git bisect</code></td>
      <td>Uses binary search to find the commit that introduced a bug.</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">git blame</code></td>
      <td>Shows what revision and author last modified each line of a file.</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">git checkout</code></td>
      <td>Switches branches or restores working tree files.</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">git checkout -b</code></td>
      <td>Creates a new branch and switches to it.</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">git cherry-pick</code></td>
      <td>Applies the changes introduced by an existing commit to your current branch.</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">git clean -fdx</code></td>
      <td>Removes untracked files from your working directory, including ignored files.</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">git clone</code></td>
      <td>Creates a copy of a remote repository on your local machine.</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">git clone --single-branch</code></td>
      <td>Clones only the specified branch from a remote repository.</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">git commit</code></td>
      <td>Records changes to the repository with a descriptive message.</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">git commit --amend -m "New commit message."</code></td>
      <td>Modifies the most recent commit with a new commit message.</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">git config</code></td>
      <td>Configures Git settings, such as user information and repository-specific options.</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">git diff</code></td>
      <td>Shows changes between commits, commit and working tree, etc.</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">git diff --check</code></td>
      <td>Checks for whitespace errors in the changes.</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">git fetch</code></td>
      <td>Downloads objects and refs from another repository without merging.</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">git gc</code></td>
      <td>Cleans up unnecessary files and optimizes the local repository.</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">git init</code></td>
      <td>Initializes a new Git repository in the current directory.</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">git log</code></td>
      <td>Displays the commit history for the current branch.</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">git log --all</code></td>
      <td>Shows the commit history for all branches.</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">git log --oneline</code></td>
      <td>Displays a simplified view of the commit history, showing one line per commit.</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">git log --summary</code></td>
      <td>Provides a summary of changes made in each commit, including file additions and deletions.</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">git log -p</code></td>
      <td>Shows the patch (diff) introduced in each commit.</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">git merge</code></td>
      <td>Combines changes from different branches into the current branch.</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">git pull --rebase</code></td>
      <td>Fetches changes from a remote repository and rebases your current branch on top of them.</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">git push</code></td>
      <td>Uploads local repository content to a remote repository.</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">git push --set-upstream origin</code></td>
      <td>Sets the default remote branch for the current local branch.</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">git push -u origin feature_branch_name</code></td>
      <td>Pushes the local branch to the remote repository and sets it to track the remote branch.</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">git rebase</code></td>
      <td>Reapplies commits on top of another base tip, allowing for a cleaner project history.</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">git remote -av</code></td>
      <td>Displays the remote repositories associated with the local repository.</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">git remote add</code></td>
      <td>Adds a new remote repository to your local Git configuration.</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">git reset --hard</code></td>
      <td>Resets the current branch to the specified state, discarding all changes in the working directory.</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">git restore</code></td>
      <td>Restores files in the working directory to their last committed state.</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">git revert</code></td>
      <td>Creates a new commit that undoes the changes made by a previous commit.</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">git rm</code></td>
      <td>Removes files from the working directory and stages the removal for the next commit.</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">git shortlog</code></td>
      <td>Summarizes the commit history, grouped by author.</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">git show</code></td>
      <td>Displays information about a specific commit, including changes and commit message.</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">git stash</code></td>
      <td>Temporarily saves changes in your working directory that are not ready to be committed.</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">git stash list</code></td>
      <td>Lists all stashed changes.</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">git stash pop</code></td>
      <td>Restores the most recently stashed changes and removes them from the stash list.</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">git status</code></td>
      <td>Displays the state of the working directory and the staging area, showing which files are staged, modified, or untracked.</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">git tag</code></td>
      <td>Creates a tag reference for a specific commit, often used for marking release points.</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">git worktree</code></td>
      <td>Manages multiple working trees attached to the same repository, allowing you to work on different branches simultaneously.</td>
    </tr>
  </tbody>
</table>]]></content><author><name>Deya Elkhawaldeh</name></author><category term="Development" /><category term="iOS" /><category term="Programming" /><category term="Swift" /><category term="Development" /><category term="iOS" /><category term="Programming" /><category term="Swift" /><summary type="html"><![CDATA[Almost anyone who uses Xcode can quickly notice that it lacks many essential Git features, which is acceptable in some way since it’s primarily a development environment rather than a dedicated source control application.]]></summary></entry><entry><title type="html">Why I Prefer To Store My Files On A Digital Ocean Space &amp;amp; Not Google Drive Or Dropbox.</title><link href="https://swiftbydeya.com/backup-on-spaces/" rel="alternate" type="text/html" title="Why I Prefer To Store My Files On A Digital Ocean Space &amp;amp; Not Google Drive Or Dropbox." /><published>2023-01-21T00:00:00+03:00</published><updated>2024-09-08T00:00:00+03:00</updated><id>https://swiftbydeya.com/backup-on-spaces</id><content type="html" xml:base="https://swiftbydeya.com/backup-on-spaces/"><![CDATA[<p>This is not directly related to swift or iOS, but thought it’s worth sharing, since I couldn’t find any article that mention such way to backup files.</p>

<p>I’m one of those who experienced the evolution of data storage firsthand, starting with floppy disks to back up HTML pages—specifically 3DMax tutorials—during my visits to internet cafés back in 2003. As technology advanced, I transitioned to using CDs, followed by DVDs, which offered greater storage capacity.</p>

<!--more-->

<!-- _includes/centered-image.html -->
<div class="centered-image">
  <img src="../images/covers/do_space_full.webp" alt="" class="centered-image__img" width="960" height="1568" />
  <p align="center">
    <em></em>
  </p>
</div>

<p>I vividly remember the first flash drive my father gifted me as a teenager; it had a mere 128 MB of storage. At that time, such a device was considered a luxury and not affordable for many where I live. Fast forward to today, and we now have SSDs that are over 1000 times larger in capacity and available at significantly lower prices.</p>

<h2 id="cloud-storage">Cloud Storage</h2>
<p>In the realm of cloud storage, common solutions like Google Drive and Dropbox offer plans, such as a 2TB option for $10 monthly. However, I prefer utilizing my own mountable drive integrated with a Content Delivery Network (CDN) for distributing my files efficiently. The best solution I’ve found for storing my work is using DigitalOcean Spaces, which is similar to AWS S3. I can conveniently mount it using a client like Cyberduck on my Mac, or on any device I own, allowing for seamless access and management of my files. This approach not only provides me with greater control over my data but also enhances my ability to share and distribute content effectively.</p>

<!-- _includes/centered-image.html -->
<div class="centered-image">
  <img src="/images/posts/image-2.webp" alt="" class="centered-image__img" />
  <p align="center">
    <em>Cyberduck, showing a Digital Ocean drive</em>
  </p>
</div>

<p>Serving static websites on DigitalOcean Spaces is entirely feasible by leveraging its S3-compatible object storage capabilities. To get started, you can upload your HTML, CSS, and JavaScript files directly to a Space, which acts as a repository for your website assets.</p>

<p>Even serving static websites, such as React applications or Jekyll blogs, on DigitalOcean Spaces is entirely feasible by leveraging its S3-compatible object storage capabilities. To get started, you can upload your website files—whether they are HTML, CSS, JavaScript. For example, when deploying a React app, you can build your project and upload the contents of the build folder to the Space. Similarly, for a Jekyll blog, you can generate the static site and upload the resulting files to your Space. Although DigitalOcean Spaces doesn’t natively support custom domains, you can set up a reverse proxy server using NGINX on a DigitalOcean Droplet to map your domain to the Space, allowing your static site to be accessed via your custom URL. This combination of tools enables you to efficiently serve your static websites while taking advantage of the scalability and performance of DigitalOcean’s infrastructure.</p>

<h2 id="pros--cons">Pros &amp; Cons</h2>

<p>When considering a storage solution, it’s essential to weigh the advantages and disadvantages to determine the best fit for your needs. DigitalOcean Spaces offers a range of benefits that make it an appealing choice for developers and businesses alike. From cost-effectiveness to enhanced control over your files, the pros of using DigitalOcean Spaces can significantly enhance your workflow and file management capabilities. Below, we outline the key advantages of utilizing DigitalOcean Spaces, as well as some potential drawbacks to keep in mind, ensuring you have a comprehensive understanding of this powerful storage solution.</p>

<h3 id="pros">Pros</h3>

<ul>
  <li>
    <p><strong>Direct Links</strong>: Easily access your files with straightforward URLs, allowing for seamless integration into your applications and workflows.</p>
  </li>
  <li>
    <p><strong>Cost-Effective</strong>: With plans starting as low as $5, DigitalOcean Spaces provides an economical solution for storing and serving your data, making it accessible for individuals and small businesses alike.</p>
  </li>
  <li>
    <p><strong>Bandwidth Savings</strong>: Setting up a Content Delivery Network (CDN) is simple, enabling you to save significant amounts of bandwidth and avoid exceeding transfer caps, which is especially beneficial for high-traffic applications.</p>
  </li>
  <li>
    <p><strong>Granular Control</strong>: You have total control over metadata and content types of your files. For example, you can specify whether an uploaded MP4 file is streamable or downloadable, tailoring the user experience to your needs.</p>
  </li>
  <li>
    <p><strong>Cross-Device Compatibility</strong>: DigitalOcean Spaces can be easily mounted on any device or server, providing flexibility and accessibility regardless of your operating system or hardware.</p>
  </li>
  <li>
    <p><strong>Active File Serving</strong>: Unlike traditional storage solutions, your files are actively served, allowing you to host dynamic content, such as an Angular website, without placing additional load on your server.</p>
  </li>
  <li>
    <p><strong>Custom URL Masking</strong>: You can mask the URL to reflect your own domain, enhancing professionalism and branding when presenting to clients or stakeholders during demos.</p>
  </li>
</ul>

<h3 id="cons">Cons</h3>

<ul>
  <li>
    <p><strong>Technical Knowledge Requirement</strong>: Some users may find that utilizing DigitalOcean Spaces requires a certain level of technical knowledge, which could be a barrier for those unfamiliar with cloud storage solutions.</p>
  </li>
  <li>
    <p><strong>Limited Client Options</strong>: Many desktop clients used to mount such drives are not open source or free, which may limit accessibility for some users.</p>
  </li>
  <li>
    <p><strong>File Sharing Limitations</strong>: Files stored in DigitalOcean Spaces cannot be shared with specific individuals; they are either public or private, which may not suit all collaboration needs.</p>
  </li>
</ul>

<h3 id="security">Security</h3>

<p>DigitalOcean Spaces offers robust security features designed to protect your data effectively. One of the key aspects is access control, which allows users to manage permissions for files stored in Spaces.</p>

<h3 id="use-cases-are-infinite">Use cases are infinite!</h3>

<p>The versatility of DigitalOcean Spaces opens up a myriad of use cases that can significantly enhance productivity and efficiency. For instance, if you’re involved in web scraping, you can effortlessly download large YouTube channels as a background job on the server. This approach allows you to avoid consuming your internet bandwidth while eliminating the need to keep a device running for extended periods. Instead, the files are stored directly in your DigitalOcean Space, making them readily accessible whenever you need them, without the hassle of local storage management. 🧐</p>

<p>Moreover, DigitalOcean Spaces functions similarly to a Network Attached Storage (NAS) solution, providing a centralized location for all your files that can be accessed from multiple devices. This capability is particularly beneficial for teams or individuals who require seamless file sharing and collaboration. Additionally, it can serve as a media center, allowing you to host and stream your multimedia content efficiently. Whether you’re managing a portfolio of projects, sharing resources with colleagues, or simply organizing personal media, the possibilities are truly endless with DigitalOcean Spaces.</p>

<p>Another innovative use case for DigitalOcean Spaces is as a centralized repository for continuous integration and deployment (CI/CD) pipelines. Software engineers can utilize Spaces to store build artifacts, such as compiled binaries, Docker images, or deployment packages, generated during the CI/CD process. By configuring your CI/CD tools to upload these artifacts directly to DigitalOcean Spaces, you can ensure that all necessary files are easily accessible for deployment across various environments. This setup not only streamlines the deployment process but also provides a reliable and scalable solution for managing versioned artifacts, making it easier to roll back to previous versions if needed. Additionally, you can integrate Spaces with other services, such as monitoring and alerting tools, to keep track of deployment statuses and ensure smooth operations throughout the software development lifecycle.</p>

<p>I’d love to hear how others are using DigitalOcean Spaces in their projects! Whether you’re leveraging it for web hosting, data storage, or as part of your CI/CD pipeline, your experiences and tips could be incredibly valuable. Share your thoughts and use cases in the comments below—let’s learn from each other!</p>]]></content><author><name>Deya Elkhawaldeh</name></author><category term="Development" /><category term="iOS" /><category term="Programming" /><category term="Swift" /><category term="Development" /><category term="iOS" /><category term="Programming" /><category term="Swift" /><summary type="html"><![CDATA[This is not directly related to swift or iOS, but thought it’s worth sharing, since I couldn’t find any article that mention such way to backup files. I’m one of those who experienced the evolution of data storage firsthand, starting with floppy disks to back up HTML pages—specifically 3DMax tutorials—during my visits to internet cafés back in 2003. As technology advanced, I transitioned to using CDs, followed by DVDs, which offered greater storage capacity.]]></summary></entry></feed>