<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Blog on Mobile app security and reliability on iOS and macOS.]]></title><description><![CDATA[Blog about mobile app security on iOS and macOS.  I sometime cover software architecture and unit tests.]]></description><link>https://blog.encoded.life</link><image><url>https://cdn.hashnode.com/res/hashnode/image/upload/v1699979708386/G5iXPfr9U.png</url><title>Blog on Mobile app security and reliability on iOS and macOS.</title><link>https://blog.encoded.life</link></image><generator>RSS for Node</generator><lastBuildDate>Wed, 13 May 2026 12:31:00 GMT</lastBuildDate><atom:link href="https://blog.encoded.life/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[A case for API-level cryptography]]></title><description><![CDATA[Do you rely on TLS Pinning or OS level HTTPS handling for all your API privacy? You may either break your app or insufficiently protect your information on corporate networks.  
It is not unusual for large corporations to use HTTPS proxy for data ins...]]></description><link>https://blog.encoded.life/a-case-for-api-level-cryptography</link><guid isPermaLink="true">https://blog.encoded.life/a-case-for-api-level-cryptography</guid><category><![CDATA[encryption]]></category><category><![CDATA[api]]></category><category><![CDATA[Security]]></category><category><![CDATA[zerotrust]]></category><category><![CDATA[Mobile Development]]></category><dc:creator><![CDATA[Dave Poirier]]></dc:creator><pubDate>Tue, 17 Feb 2026 16:36:05 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1771346418062/0ba85541-2ace-4416-8063-eb230e79327d.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Do you rely on TLS Pinning or OS level HTTPS handling for all your API privacy? You may either break your app or insufficiently protect your information on corporate networks.  </p>
<p>It is not unusual for large corporations to use HTTPS proxy for data inspection. According to a paper titled "The Sorry State of TLS Security in Enterprise Interception": Network traffic inspection, including TLS traffic, in enterprise environments is widely practiced. — <a target="_blank" href="https://dl.acm.org/doi/10.1145/3372802">https://dl.acm.org/doi/10.1145/3372802</a>  </p>
<p>According to data reported by Cloudflare, "between 4% and 10% of the web’s encrypted traffic (HTTPS) is intercepted." — <a target="_blank" href="https://blog.cloudflare.com/understanding-the-prevalence-of-web-traffic-interception/">https://blog.cloudflare.com/understanding-the-prevalence-of-web-traffic-interception/</a>  </p>
<p>The impact on your API communication can be either exposing your user's private data to unintended third-party observers, or service interruption if you are doing TLS pinning.  </p>
<p>This is why I have been pushing for API level security, using public/private keys, building on the foundations of Zero-Trust Architecture.  </p>
<p>When building your next API, if you are transferring PII (Personally Identifiable Information) you shouldn't rely on the transport layer to provide the security. It should be built into your infrastructure and API layer.</p>
<hr />
<h2 id="heading-zero-trust-architecture-a-quick-primer"><strong>Zero-Trust Architecture: A Quick Primer</strong></h2>
<p>Zero-Trust is a security model built on a simple premise: no network location, device, or connection is inherently trusted. Every request must prove its legitimacy, every time.</p>
<p>The concept was formalized by NIST in SP 800-207 ("Zero Trust Architecture"), which lays out the core tenets: verify explicitly, use least-privilege access, and assume breach. The traditional perimeter-based model "everything inside the firewall is safe" falls apart when your traffic is being inspected, rerouted, or decrypted by middleboxes you don't control.</p>
<p>This is directly relevant to API security. If your confidentiality guarantee lives entirely in the transport layer, then anyone who terminates that TLS session — legitimately or not — sees everything in plaintext. Zero-Trust pushes that trust boundary down to the application itself. The data should be protected independent of the pipe it travels through.</p>
<p>If you want to go deeper, NIST SP 800-207 is the canonical starting point (<a target="_blank" href="https://csrc.nist.gov/pubs/sp/800/207/final">https://csrc.nist.gov/pubs/sp/800/207/final</a>). For a more practical take, the CISA Zero Trust Maturity Model gives a staged adoption framework (<a target="_blank" href="https://www.cisa.gov/zero-trust-maturity-model">https://www.cisa.gov/zero-trust-maturity-model</a>).</p>
<hr />
<h2 id="heading-strategies-for-api-level-cryptography"><strong>Strategies for API-Level Cryptography</strong></h2>
<h3 id="heading-web-apps">Web Apps</h3>
<p>JWE (JSON Web Encryption, RFC 7516) is a well-supported standard for this. You encrypt the payload using the recipient's public key, and only their corresponding private key can decrypt it. Libraries exist for virtually every major language and platform.</p>
<p>For simpler cases, you can use a hybrid approach: exchange a symmetric key using asymmetric encryption (e.g., ECDH or RSA key exchange), then encrypt payloads with AES-GCM. This is essentially what TLS does internally. You're just moving that negotiation into your application protocol.</p>
<p>Encryption gives you confidentiality, but you also need to know the message hasn't been tampered with and actually came from who it claims to. Message signing addresses this.</p>
<p>JWS (JSON Web Signature, RFC 7515) lets the sender sign a payload with their private key, and the recipient verifies with the sender's public key. This proves both origin and integrity. You can combine JWS and JWE (sign-then-encrypt) for both properties.</p>
<p>HTTP Message Signatures (RFC 9421) is a newer standard worth watching, it lets you sign not just the body but also specific HTTP headers, method, and path, which protects against replay and request-manipulation attacks that payload-only signing misses.</p>
<h3 id="heading-mobile-apps">Mobile Apps</h3>
<p>For mobile applications, you can go a step further than software-only cryptography by leveraging hardware-backed key storage. Both iOS and Android offer secure hardware enclaves that can generate, store, and use cryptographic keys in a way that the private key material never leaves the hardware — not even your own app can extract it.</p>
<h4 id="heading-ios-secure-enclave-cryptokit">iOS: Secure Enclave + CryptoKit</h4>
<p>On iPhone, the Secure Enclave supports P256 elliptic curve keys for both key agreement (ECDH) and signing (ECDSA). AES-GCM is hardware accelerated on Apple silicon. The general flow:</p>
<ol>
<li><p>On the server side, generate an ECC P256 key pair in an HSM (or a cloud KMS such as AWS KMS or GCP Cloud HSM). Publish the public key through a key discovery endpoint, including a key ID for rotation. Avoid hardcoding the key in your app binary, as this creates the same update-to-rotate problem as TLS pinning.</p>
</li>
<li><p>In your mobile app, use the Secure Enclave to generate a hardware-bound P256 key agreement key pair via CryptoKit's <code>SecureEnclave.P256.KeyAgreement.PrivateKey</code>.</p>
</li>
<li><p>Perform ECDH between the client's Secure Enclave private key and the server's public key to derive a shared secret.</p>
</li>
<li><p>Use HKDF to derive AES symmetric keys from that shared secret. Be deliberate about the HKDF parameters: use a unique salt per session and include context in the info parameter (e.g., client identifier, server identifier, and key purpose) to bind the derived key to a specific use.</p>
</li>
<li><p>Encrypt payloads using AES-GCM, which provides both confidentiality and integrity in a single operation, with fallback to AES-CTR + HMAC-SHA256 if you require streaming encryption.</p>
</li>
</ol>
<p>For request authenticity, the Secure Enclave also supports P256 signing (<code>SecureEnclave.P256.Signing.PrivateKey</code>). Have the client sign each request (or a digest of it) so the server can verify the request genuinely came from a device holding the corresponding private key. This gives you the mobile equivalent of the message signing pattern described in the web section above.</p>
<p>A note on algorithm choice: prefer AES-GCM over AES-CTR. AES-CTR provides confidentiality only, it has no built-in integrity protection, meaning a payload encrypted with CTR alone can be silently modified in transit. If you require the ability to stream data for encryption, for example encrypting very large file in an app extension, you may have to fallback to AES-CTR and implement your own integrity validation using HMAC-SHA256 which is also hardware accelerated.</p>
<h4 id="heading-android-keystore-strongbox">Android: Keystore + StrongBox</h4>
<p>Android offers a similar model through the Android Keystore system, with StrongBox providing hardware-level protection on supported devices (Pixel 3+, Samsung Galaxy S9+, and most flagships since 2018).</p>
<ol>
<li><p>Generate a P256 key pair in the Android Keystore with <code>setIsStrongBoxBacked(true)</code>where available (falling back to TEE-backed Keystore otherwise).</p>
</li>
<li><p>Use <code>KeyAgreement</code> with ECDH to derive a shared secret against the server's public key.</p>
</li>
<li><p>Derive AES keys using HKDF with the same parameter discipline as on iOS.</p>
</li>
<li><p>Encrypt with AES-GCM via <code>Cipher.getInstance("AES/GCM/NoPadding")</code>.</p>
</li>
</ol>
<p>For signing, the Android Keystore supports ECDSA with P256 (<code>SHA256withECDSA</code>), giving you the same request authentication capability.</p>
<h3 id="heading-key-discovery-and-rotation">Key Discovery and Rotation</h3>
<p>A natural question is how the app gets the server's public key in the first place, and how you rotate it over time. Bundling the operational encryption key directly in your app binary works initially, but creates the same update-to-rotate brittleness as TLS pinning.</p>
<p>The temptation is to solve this with a key discovery endpoint, fetching the latest key when you encounter an unknown key ID. But this reintroduces the problem you are trying to solve: that request isn't protected by your API-level encryption, so you're trusting the transport layer for the most sensitive exchange of all.</p>
<p>The approach that avoids this chicken-and-egg problem is to separate the trust anchor from the rotatable keys:</p>
<p>Bundle a long-lived <strong>signing-only public key</strong> in your app. This key does not encrypt anything. Its sole purpose is to verify the authenticity of other keys. Store its private counterpart in an HSM, use it for nothing else, and treat it as a root of trust.</p>
<p>Your key discovery endpoint then serves rotatable encryption and key-agreement public keys, each accompanied by a signature produced by the long-lived signing key and a key ID. When the app encounters an unfamiliar key ID, it fetches the new key from the endpoint and verifies the signature against the bundled trust anchor before accepting it. If the signature doesn't verify, the key is rejected regardless of what the transport layer says.</p>
<p>This gives you the ability to rotate operational keys at will, no app update required, while keeping trust rooted in a narrow, infrequently-changing anchor. The only scenario that forces an app update is compromise of the signing key itself, which is a much rarer event when that key lives in an HSM and is used exclusively for signing other keys.</p>
<p>It doesn't eliminate the bundled-key dependency entirely, but it reduces it to the smallest possible surface: a single verification key with a long lifespan and a minimal attack profile.</p>
<hr />
<h2 id="heading-conclusion"><strong>Conclusion</strong></h2>
<p>Transport-layer security is necessary but not sufficient. If your API handles sensitive data, and most do, the confidentiality and authenticity guarantees should live where the data lives: in the application layer. The standards and tooling are mature enough that this is no longer a heroic engineering effort. It's a design choice.</p>
<p>Start by identifying which API endpoints carry PII or other sensitive payloads. You don't need to encrypt everything, focus on where the exposure actually matters. Pick the strategy that fits your platform: JWE/JWS for web applications with broad library support, hardware-backed cryptography for mobile where the Secure Enclave and Android Keystore give you stronger guarantees. Design for key rotation from day one, because the alternative is painting yourself into the same corner that TLS pinning creates.</p>
<p>The building blocks are all standardized and well-documented. What's been missing is the habit of reaching for them.</p>
<p><strong>Further Reading</strong></p>
<ul>
<li><p><strong>NIST SP 800-207 — Zero Trust Architecture</strong>: The foundational document on Zero-Trust principles. Free to read at <a target="_blank" href="https://csrc.nist.gov/pubs/sp/800/207/final">https://csrc.nist.gov/pubs/sp/800/207/final</a></p>
</li>
<li><p><strong>CISA Zero Trust Maturity Model</strong>: A practical staged framework for adopting Zero-Trust in your organization. Available at <a target="_blank" href="https://www.cisa.gov/zero-trust-maturity-model">https://www.cisa.gov/zero-trust-maturity-model</a></p>
</li>
<li><p><strong>RFC 7516 (JWE) and RFC 7515 (JWS)</strong>: The IETF standards for JSON Web Encryption and JSON Web Signature, the backbone of web application payload security.</p>
</li>
<li><p><strong>RFC 9421 — HTTP Message Signatures</strong>: A newer standard for signing HTTP message components including headers, method, and path — useful for request authenticity beyond just the payload. <a target="_blank" href="https://www.rfc-editor.org/rfc/rfc9421">https://www.rfc-editor.org/rfc/rfc9421</a></p>
</li>
<li><p><strong>Apple CryptoKit — SecureEnclave</strong>: Apple's documentation on hardware-backed P256 key agreement and signing. <a target="_blank" href="https://developer.apple.com/documentation/cryptokit/secureenclave">https://developer.apple.com/documentation/cryptokit/secureenclave</a></p>
</li>
<li><p><strong>Android Keystore System</strong>: Android's documentation on hardware-backed key storage, including StrongBox support. <a target="_blank" href="https://developer.android.com/privacy-and-security/keystore">https://developer.android.com/privacy-and-security/keystore</a></p>
</li>
<li><p><strong>"The Sorry State of TLS Security in Enterprise Interception"</strong>: The research paper referenced at the top of this post, covering the prevalence and risks of TLS interception in corporate environments. <a target="_blank" href="https://dl.acm.org/doi/10.1145/3372802">https://dl.acm.org/doi/10.1145/3372802</a></p>
</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Claude Code, better in pair]]></title><description><![CDATA[When using Claude Code in terminal, I always have two separate Claude code sessions opened, each with their own context.  
When one session gives me an implementation plan, I copy/paste the plan into the other and ask for a review of the plan for any...]]></description><link>https://blog.encoded.life/claude-code-better-in-pair-plan-review-improvement</link><guid isPermaLink="true">https://blog.encoded.life/claude-code-better-in-pair-plan-review-improvement</guid><category><![CDATA[claude.ai]]></category><category><![CDATA[AI]]></category><category><![CDATA[software development]]></category><dc:creator><![CDATA[Dave Poirier]]></dc:creator><pubDate>Thu, 05 Feb 2026 16:26:20 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1770308625018/e96691fb-1bde-4a6c-b9f4-37362a98859b.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>When using Claude Code in terminal, I always have two separate Claude code sessions opened, each with their own context.  </p>
<p>When one session gives me an implementation plan, I copy/paste the plan into the other and ask for a review of the plan for any issues.  </p>
<p>While the AI attempts to review its own work, during the development of the plan it made assumptions and may have poisoned its own context with those decisions. But when you paste the plan in a new context and ask for analysis, this new context will not have those assumptions known and may point out interesting findings.  </p>
<p>As a "Human in the loop", I decide whether the feedback on the plan requires me to go back to the original drawing board, or if I should just provide the feedback to the first session to self-correct.  </p>
<p>This is also a really good time to re-evaluate what changes would have been required in the original inputs (your CLAUDE.md, Skills, MCP or your prompt) to avoid the highlighted issues. Sometimes, it means scrapping the entire session, and changing your original prompt to contain more information.  </p>
<p>This process is what leads me to use external markdown files most of the time for my tasks, then just ask the AI to read the markdown file to know what is needed. This way, if any corrections are needed, I always have access to the original prompt data (in the markdown file).  </p>
<p>I treat AI as a genius brand new developer joining your team. It doesn't matter how good they are, without context they won't be able to succeed. So knowing what to add, or what to remove, is what will make your AI session succeed or fail.  </p>
<p><a target="_blank" href="https://www.linkedin.com/search/results/all/?keywords=%23humanintheloop&amp;origin=HASH_TAG_FROM_FEED">#HumanInTheLoop</a> <a target="_blank" href="https://www.linkedin.com/search/results/all/?keywords=%23aidevelopment&amp;origin=HASH_TAG_FROM_FEED">#AIDevelopment</a> <a target="_blank" href="https://www.linkedin.com/search/results/all/?keywords=%23codequality&amp;origin=HASH_TAG_FROM_FEED">#CodeQuality</a></p>
]]></content:encoded></item><item><title><![CDATA[Strategies for dealing with Swift actor data races]]></title><description><![CDATA[When Swift introduced actors in Swift 5.5, many developers breathed a sigh of relief. Finally, a language-level construct that would protect us from data races! But here's the uncomfortable truth: actors don't eliminate data races—they just change wh...]]></description><link>https://blog.encoded.life/strategies-for-dealing-with-swift-actor-data-races</link><guid isPermaLink="true">https://blog.encoded.life/strategies-for-dealing-with-swift-actor-data-races</guid><category><![CDATA[Swift]]></category><category><![CDATA[concurrency]]></category><category><![CDATA[software development]]></category><dc:creator><![CDATA[Dave Poirier]]></dc:creator><pubDate>Wed, 28 Jan 2026 18:10:38 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1769623708762/bf8f1615-c4fb-4894-92bc-e6ef474ce4d0.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>When Swift introduced actors in Swift 5.5, many developers breathed a sigh of relief. Finally, a language-level construct that would protect us from data races! But here's the uncomfortable truth: <strong>actors don't eliminate data races—they just change where they can occur.</strong></p>
<p>The culprit? <strong>Re-entrancy.</strong></p>
<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text">Article written by AI under human supervision &amp; guidance.</div>
</div>

<h2 id="heading-the-false-sense-of-security">The False Sense of Security</h2>
<p>Consider this innocent-looking actor:</p>
<pre><code class="lang-swift">actor <span class="hljs-type">BankAccount</span> {
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">var</span> balance: <span class="hljs-type">Double</span> = <span class="hljs-number">1000</span>

    <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">withdraw</span><span class="hljs-params">(<span class="hljs-number">_</span> amount: Double)</span></span> async -&gt; <span class="hljs-type">Bool</span> {
        <span class="hljs-comment">// Check if we have sufficient funds</span>
        <span class="hljs-keyword">guard</span> balance &gt;= amount <span class="hljs-keyword">else</span> {
            <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>
        }

        <span class="hljs-comment">// Simulate an async operation (logging, validation, network call, etc.)</span>
        await performAsyncValidation()

        <span class="hljs-comment">// Deduct the amount</span>
        balance -= amount
        <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>
    }

    <span class="hljs-keyword">private</span> <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">performAsyncValidation</span><span class="hljs-params">()</span></span> async {
        <span class="hljs-keyword">try</span>? await <span class="hljs-type">Task</span>.sleep(<span class="hljs-keyword">for</span>: .milliseconds(<span class="hljs-number">100</span>))
    }
}
</code></pre>
<p>Looks safe, right? The actor should serialize access to <code>balance</code>. Let's test it:</p>
<pre><code class="lang-swift"><span class="hljs-keyword">let</span> account = <span class="hljs-type">BankAccount</span>()

async <span class="hljs-keyword">let</span> withdrawal1 = account.withdraw(<span class="hljs-number">600</span>)
async <span class="hljs-keyword">let</span> withdrawal2 = account.withdraw(<span class="hljs-number">600</span>)

<span class="hljs-keyword">let</span> results = await [withdrawal1, withdrawal2]
<span class="hljs-built_in">print</span>(results) <span class="hljs-comment">// [true, true] — Wait, what?!</span>

<span class="hljs-built_in">print</span>(await account.getBalance()) <span class="hljs-comment">// 💥 -200</span>
</code></pre>
<p><strong>Both withdrawals succeeded, and we've gone negative.</strong> This is a data race.</p>
<h2 id="heading-why-does-this-happen-the-mailbox-model">Why Does This Happen? The Mailbox Model</h2>
<p>Actors only do one thing at a time—that's true. But actors also <strong>don't like to sit around doing nothing</strong>. Think of an actor as having a "mailbox" of pending work. When you call an actor method, you're dropping a message into that mailbox.</p>
<p>Here's the key insight: <strong>when an actor method hits an</strong> <code>await</code>, it suspends—and the actor immediately picks up the next message from its mailbox rather than waiting idle.</p>
<p>Here's the sequence that leads to our bug:</p>
<ol>
<li><p><code>withdraw(600)</code> #1 starts, checks <code>balance &gt;= 600</code> ✅ (balance is 1000)</p>
</li>
<li><p><code>withdraw(600)</code> #1 hits <code>await performAsyncValidation()</code> and <strong>suspends</strong></p>
</li>
<li><p><strong>Actor picks up next message:</strong> <code>withdraw(600)</code> #2 starts (re-entrancy!)</p>
</li>
<li><p>#2 checks <code>balance &gt;= 600</code> ✅ (balance is <em>still</em> 1000—#1 hasn't modified it yet)</p>
</li>
<li><p>#2 hits <code>await</code> and suspends</p>
</li>
<li><p>#1 resumes, deducts 600 → balance is now 400</p>
</li>
<li><p>#2 resumes, deducts 600 → balance is now -200 💥</p>
</li>
</ol>
<p>The <code>guard</code> check and the mutation are not atomic across the <code>await</code> boundary. The actor only guarantees that <strong>synchronous</strong>code blocks don't interleave.</p>
<h2 id="heading-two-flavors-of-re-entrancy-problems">Two Flavors of Re-Entrancy Problems</h2>
<p>Re-entrancy can cause two distinct types of issues:</p>
<h3 id="heading-problem-1-incorrect-state-data-corruption">Problem 1: Incorrect State (Data Corruption)</h3>
<p>This is what we saw above—multiple operations interleave and corrupt shared state, leading to invalid results like a negative bank balance.</p>
<h3 id="heading-problem-2-wasteful-duplicate-work">Problem 2: Wasteful Duplicate Work</h3>
<p>Even when state doesn't get corrupted, re-entrancy can cause unnecessary work. Consider a cache that fetches from a remote server:</p>
<pre><code class="lang-swift">actor <span class="hljs-type">DataCache</span> {
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">var</span> cache: [<span class="hljs-type">UUID</span>: <span class="hljs-type">Data</span>] = [:]

    <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">read</span><span class="hljs-params">(<span class="hljs-number">_</span> key: UUID)</span></span> async -&gt; <span class="hljs-type">Data?</span> {
        <span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> data = cache[key] {
            <span class="hljs-keyword">return</span> data
        }

        <span class="hljs-comment">// Fetch from remote if not cached locally</span>
        <span class="hljs-keyword">guard</span> <span class="hljs-keyword">let</span> data = <span class="hljs-keyword">try</span>? await fetchFromServer(key) <span class="hljs-keyword">else</span> {
            <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>
        }

        cache[key] = data
        <span class="hljs-keyword">return</span> data
    }
}
</code></pre>
<p>When multiple concurrent reads request the same uncached key:</p>
<pre><code class="lang-plaintext">cache read called for DDFA2377-...
attempt to read remote cache for DDFA2377-...
cache read called for DDFA2377-...          // Re-entrancy!
attempt to read remote cache for DDFA2377-... // Duplicate request!
cache read called for DDFA2377-...          // Re-entrancy again!
attempt to read remote cache for DDFA2377-... // Another duplicate!
</code></pre>
<p>We made <strong>three network requests</strong> when one would have sufficed. The first request would have cached the result for the others—but they all started before any completed.</p>
<hr />
<p>Now let's look at strategies to handle both types of problems.</p>
<h2 id="heading-strategy-1-bounce-concurrent-requests">Strategy 1: Bounce Concurrent Requests</h2>
<p>The simplest approach: if an operation is already in progress, reject new requests immediately.</p>
<pre><code class="lang-swift">actor <span class="hljs-type">BankAccount</span> {
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">var</span> balance: <span class="hljs-type">Double</span> = <span class="hljs-number">1000</span>
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">var</span> isWithdrawing = <span class="hljs-literal">false</span>

    <span class="hljs-class"><span class="hljs-keyword">enum</span> <span class="hljs-title">WithdrawError</span>: <span class="hljs-title">Error</span> </span>{
        <span class="hljs-keyword">case</span> operationInProgress
        <span class="hljs-keyword">case</span> insufficientFunds
    }

    <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">withdraw</span><span class="hljs-params">(<span class="hljs-number">_</span> amount: Double)</span></span> async <span class="hljs-keyword">throws</span> -&gt; <span class="hljs-type">Double</span> {
        <span class="hljs-comment">// Reject if another withdrawal is in progress</span>
        <span class="hljs-keyword">guard</span> !isWithdrawing <span class="hljs-keyword">else</span> {
            <span class="hljs-keyword">throw</span> <span class="hljs-type">WithdrawError</span>.operationInProgress
        }

        isWithdrawing = <span class="hljs-literal">true</span>
        <span class="hljs-keyword">defer</span> { isWithdrawing = <span class="hljs-literal">false</span> }

        <span class="hljs-keyword">guard</span> balance &gt;= amount <span class="hljs-keyword">else</span> {
            <span class="hljs-keyword">throw</span> <span class="hljs-type">WithdrawError</span>.insufficientFunds
        }

        await performAsyncValidation()

        balance -= amount
        <span class="hljs-keyword">return</span> balance
    }
}
</code></pre>
<p><strong>When to use:</strong> Idempotent operations, UI button debouncing, preventing duplicate submissions.</p>
<p><strong>Pros:</strong> Simple, explicit, fast failure.</p>
<p><strong>Cons:</strong> Callers must handle rejection and potentially retry.</p>
<h2 id="heading-strategy-2-queue-and-await-previous-operations">Strategy 2: Queue and Await Previous Operations</h2>
<p>Instead of rejecting concurrent requests, queue them up and process them serially.</p>
<pre><code class="lang-swift">actor <span class="hljs-type">BankAccount</span> {
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">var</span> balance: <span class="hljs-type">Double</span> = <span class="hljs-number">1000</span>
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">var</span> pendingOperations: [<span class="hljs-type">CheckedContinuation</span>&lt;<span class="hljs-type">Void</span>, <span class="hljs-type">Never</span>&gt;] = []
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">var</span> isOperationInProgress = <span class="hljs-literal">false</span>

    <span class="hljs-keyword">private</span> <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">acquireLock</span><span class="hljs-params">()</span></span> async {
        <span class="hljs-keyword">if</span> isOperationInProgress {
            <span class="hljs-comment">// Wait our turn</span>
            await withCheckedContinuation { continuation <span class="hljs-keyword">in</span>
                pendingOperations.append(continuation)
            }
        }
        isOperationInProgress = <span class="hljs-literal">true</span>
    }

    <span class="hljs-keyword">private</span> <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">releaseLock</span><span class="hljs-params">()</span></span> {
        <span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> next = pendingOperations.first {
            pendingOperations.removeFirst()
            next.resume() <span class="hljs-comment">// Wake up the next waiter</span>
        } <span class="hljs-keyword">else</span> {
            isOperationInProgress = <span class="hljs-literal">false</span>
        }
    }

    <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">withdraw</span><span class="hljs-params">(<span class="hljs-number">_</span> amount: Double)</span></span> async -&gt; <span class="hljs-type">Result</span>&lt;<span class="hljs-type">Double</span>, <span class="hljs-type">WithdrawError</span>&gt; {
        await acquireLock()
        <span class="hljs-keyword">defer</span> { releaseLock() }

        <span class="hljs-keyword">guard</span> balance &gt;= amount <span class="hljs-keyword">else</span> {
            <span class="hljs-keyword">return</span> .failure(.insufficientFunds)
        }

        await performAsyncValidation()

        balance -= amount
        <span class="hljs-keyword">return</span> .success(balance)
    }
}
</code></pre>
<p><strong>When to use:</strong> When all requests must eventually be processed, order matters, or you need transactional semantics.</p>
<p><strong>Pros:</strong> No requests are dropped; guaranteed serialization.</p>
<p><strong>Cons:</strong> Increased latency for queued requests; potential for queue buildup.</p>
<h2 id="heading-strategy-3-optimistic-execution-with-rollback">Strategy 3: Optimistic Execution with Rollback</h2>
<p>Sometimes you want to proceed optimistically and verify/rollback if conditions changed during the async operation.</p>
<pre><code class="lang-swift">actor <span class="hljs-type">BankAccount</span> {
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">var</span> balance: <span class="hljs-type">Double</span> = <span class="hljs-number">1000</span>
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">var</span> transactionLog: [<span class="hljs-type">UUID</span>: <span class="hljs-type">Double</span>] = [:]

    <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">withdraw</span><span class="hljs-params">(<span class="hljs-number">_</span> amount: Double)</span></span> async -&gt; <span class="hljs-type">Result</span>&lt;<span class="hljs-type">Double</span>, <span class="hljs-type">WithdrawError</span>&gt; {
        <span class="hljs-comment">// Initial validation</span>
        <span class="hljs-keyword">guard</span> balance &gt;= amount <span class="hljs-keyword">else</span> {
            <span class="hljs-keyword">return</span> .failure(.insufficientFunds)
        }

        <span class="hljs-comment">// Capture pre-await state</span>
        <span class="hljs-keyword">let</span> transactionId = <span class="hljs-type">UUID</span>()
        <span class="hljs-keyword">let</span> balanceBefore = balance

        <span class="hljs-comment">// Optimistically reserve the funds</span>
        balance -= amount
        transactionLog[transactionId] = amount

        <span class="hljs-comment">// Perform async work</span>
        <span class="hljs-keyword">let</span> validationPassed = await performAsyncValidation()

        <span class="hljs-comment">// Post-await verification</span>
        <span class="hljs-keyword">let</span> stateCorrupted = balance &lt; <span class="hljs-number">0</span>
        <span class="hljs-keyword">let</span> validationFailed = !validationPassed

        <span class="hljs-keyword">if</span> stateCorrupted || validationFailed {
            <span class="hljs-comment">// Rollback</span>
            <span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> reserved = transactionLog.removeValue(forKey: transactionId) {
                balance += reserved
            }
            <span class="hljs-keyword">return</span> .failure(validationFailed ? .validationFailed : .insufficientFunds)
        }

        <span class="hljs-comment">// Commit</span>
        transactionLog.removeValue(forKey: transactionId)
        <span class="hljs-keyword">return</span> .success(balance)
    }
}
</code></pre>
<p><strong>When to use:</strong> When async operations are expensive and you want to maximize throughput; when rollback is cheap.</p>
<p><strong>Pros:</strong> Maximum concurrency, no blocking.</p>
<p><strong>Cons:</strong> Rollback logic can be complex; may waste work on rolled-back transactions.</p>
<h2 id="heading-strategy-4-re-validate-after-await">Strategy 4: Re-validate After Await</h2>
<p>A simpler variant of Strategy 3—just re-check your preconditions after awaiting:</p>
<pre><code class="lang-swift">actor <span class="hljs-type">BankAccount</span> {
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">var</span> balance: <span class="hljs-type">Double</span> = <span class="hljs-number">1000</span>

    <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">withdraw</span><span class="hljs-params">(<span class="hljs-number">_</span> amount: Double)</span></span> async -&gt; <span class="hljs-type">Result</span>&lt;<span class="hljs-type">Double</span>, <span class="hljs-type">WithdrawError</span>&gt; {
        <span class="hljs-comment">// First check</span>
        <span class="hljs-keyword">guard</span> balance &gt;= amount <span class="hljs-keyword">else</span> {
            <span class="hljs-keyword">return</span> .failure(.insufficientFunds)
        }

        <span class="hljs-keyword">let</span> balanceSnapshot = balance

        await performAsyncValidation()

        <span class="hljs-comment">// Re-validate after await</span>
        <span class="hljs-keyword">guard</span> balance == balanceSnapshot <span class="hljs-keyword">else</span> {
            <span class="hljs-comment">// State changed during await—abort or retry</span>
            <span class="hljs-keyword">return</span> .failure(.stateChanged)
        }

        <span class="hljs-keyword">guard</span> balance &gt;= amount <span class="hljs-keyword">else</span> {
            <span class="hljs-keyword">return</span> .failure(.insufficientFunds)
        }

        balance -= amount
        <span class="hljs-keyword">return</span> .success(balance)
    }
}
</code></pre>
<p><strong>When to use:</strong> When you can afford to fail and have the caller retry.</p>
<p><strong>Pros:</strong> Very simple; no complex state tracking.</p>
<p><strong>Cons:</strong> May require retry logic; can fail even when it theoretically could have succeeded.</p>
<h2 id="heading-strategy-5-coalesce-with-in-progress-task-tracking">Strategy 5: Coalesce with In-Progress Task Tracking</h2>
<p>This elegant pattern solves the "duplicate work" problem by tracking in-flight operations. Subsequent requests for the same resource await the existing task rather than starting a new one.</p>
<p>The key insight is to store the <strong>task itself</strong> (not just a boolean flag) so concurrent callers can await the same result:</p>
<pre><code class="lang-swift">actor <span class="hljs-type">DataCache</span> {
    <span class="hljs-class"><span class="hljs-keyword">enum</span> <span class="hljs-title">CacheEntry</span> </span>{
        <span class="hljs-keyword">case</span> inProgress(<span class="hljs-type">Task</span>&lt;<span class="hljs-type">Data?</span>, <span class="hljs-type">Error</span>&gt;)
        <span class="hljs-keyword">case</span> loaded(<span class="hljs-type">Data</span>)
    }

    <span class="hljs-keyword">private</span> <span class="hljs-keyword">var</span> cache: [<span class="hljs-type">UUID</span>: <span class="hljs-type">CacheEntry</span>] = [:]

    <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">read</span><span class="hljs-params">(<span class="hljs-number">_</span> key: UUID)</span></span> async -&gt; <span class="hljs-type">Data?</span> {
        <span class="hljs-comment">// Already have the data? Return immediately.</span>
        <span class="hljs-keyword">if</span> <span class="hljs-keyword">case</span> <span class="hljs-keyword">let</span> .loaded(data) = cache[key] {
            <span class="hljs-keyword">return</span> data
        }

        <span class="hljs-comment">// Already fetching? Await the existing task.</span>
        <span class="hljs-keyword">if</span> <span class="hljs-keyword">case</span> <span class="hljs-keyword">let</span> .inProgress(task) = cache[key] {
            <span class="hljs-keyword">return</span> <span class="hljs-keyword">try</span>? await task.value
        }

        <span class="hljs-comment">// Start a new fetch and store the task immediately (before awaiting!)</span>
        <span class="hljs-keyword">let</span> task: <span class="hljs-type">Task</span>&lt;<span class="hljs-type">Data?</span>, <span class="hljs-type">Error</span>&gt; = <span class="hljs-type">Task</span> {
            <span class="hljs-keyword">try</span> await fetchFromServer(key)
        }

        cache[key] = .inProgress(task)

        <span class="hljs-comment">// Now await our own task</span>
        <span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> data = <span class="hljs-keyword">try</span>? await task.value {
            cache[key] = .loaded(data)
            <span class="hljs-keyword">return</span> data
        } <span class="hljs-keyword">else</span> {
            cache[key] = <span class="hljs-literal">nil</span>
            <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>
        }
    }
}
</code></pre>
<p>Now concurrent reads coalesce into a single network request:</p>
<pre><code class="lang-plaintext">cache read called for DDFA2377-...
cache read called for DDFA2377-...  // Sees .inProgress, awaits same task
cache read called for DDFA2377-...  // Sees .inProgress, awaits same task
attempt to read remote cache for DDFA2377-...  // Only ONE network call!
remote cache HIT for DDFA2377-...
cache read finished for DDFA2377-...
cache read finished for DDFA2377-...
cache read finished for DDFA2377-...
</code></pre>
<p><strong>When to use:</strong> Caching, token refresh flows, any idempotent fetch where duplicate requests are wasteful.</p>
<p><strong>Pros:</strong> Eliminates duplicate work; all callers get the same result; elegant state machine.</p>
<p><strong>Cons:</strong> Slightly more complex; requires thinking about task lifecycle.</p>
<p><em>This pattern is explained in depth in</em> <a target="_blank" href="https://www.donnywals.com/actor-reentrancy-in-swift-explained/"><em>Donny Wals' excellent article on actor re-entrancy</em></a><em>, which also covers practical applications like token refresh flows and image loaders.</em></p>
<h2 id="heading-strategy-6-immutable-state-version-compare-and-swap">Strategy 6: Immutable State + Version (Compare-and-Swap)</h2>
<p>Instead of mutating state, use a version number to detect concurrent modifications:</p>
<pre><code class="lang-swift">actor <span class="hljs-type">BankAccount</span> {
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">var</span> state: <span class="hljs-type">AccountState</span>

    <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">AccountState</span>: <span class="hljs-title">Sendable</span> </span>{
        <span class="hljs-keyword">let</span> balance: <span class="hljs-type">Double</span>
        <span class="hljs-keyword">let</span> version: <span class="hljs-type">Int</span>
    }

    <span class="hljs-keyword">init</span>(balance: <span class="hljs-type">Double</span>) {
        <span class="hljs-keyword">self</span>.state = <span class="hljs-type">AccountState</span>(balance: balance, version: <span class="hljs-number">0</span>)
    }

    <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">withdraw</span><span class="hljs-params">(<span class="hljs-number">_</span> amount: Double)</span></span> async -&gt; <span class="hljs-type">Result</span>&lt;<span class="hljs-type">Double</span>, <span class="hljs-type">WithdrawError</span>&gt; {
        <span class="hljs-keyword">let</span> snapshot = state

        <span class="hljs-keyword">guard</span> snapshot.balance &gt;= amount <span class="hljs-keyword">else</span> {
            <span class="hljs-keyword">return</span> .failure(.insufficientFunds)
        }

        await performAsyncValidation()

        <span class="hljs-comment">// Compare-and-swap: reject if state changed</span>
        <span class="hljs-keyword">guard</span> state.version == snapshot.version <span class="hljs-keyword">else</span> {
            <span class="hljs-keyword">return</span> .failure(.stateChanged)
        }

        state = <span class="hljs-type">AccountState</span>(
            balance: snapshot.balance - amount,
            version: snapshot.version + <span class="hljs-number">1</span>
        )
        <span class="hljs-keyword">return</span> .success(state.balance)
    }
}
</code></pre>
<p><strong>When to use:</strong> Complex state objects; when you want clear semantics about what constitutes a "change."</p>
<p><strong>Pros:</strong> Clear state transitions; easy to debug; works well with audit trails.</p>
<p><strong>Cons:</strong> Requires immutable state design; version conflicts need retry logic.</p>
<h2 id="heading-choosing-the-right-strategy">Choosing the Right Strategy</h2>
<p><strong>Bounce</strong> → Debouncing, idempotent ops • Low complexity • High throughput (fails fast)</p>
<p><strong>Queue</strong> → Ordered processing, transactions • Medium complexity • Low throughput (serialized)</p>
<p><strong>Optimistic + Rollback</strong> → High-throughput systems, cheap rollback • High complexity • High throughput</p>
<p><strong>Re-validate</strong> → Simple operations, retriable • Low complexity • Medium throughput</p>
<p><strong>Coalesce (Task Tracking)</strong> → Caching, deduplication • Medium complexity • High throughput</p>
<p><strong>Immutable + Version</strong> → Complex state, audit trails • Medium complexity • Medium throughput</p>
<h2 id="heading-key-takeaways">Key Takeaways</h2>
<ol>
<li><p><strong>Actors protect synchronous access</strong>, not sequences of operations spanning <code>await</code>.</p>
</li>
<li><p><strong>Think "mailbox"</strong>: when your method suspends, the actor immediately processes the next queued message.</p>
</li>
<li><p><strong>Two problems to watch for</strong>: incorrect state (data corruption) and wasteful duplicate work.</p>
</li>
<li><p><strong>The golden question</strong>: Every time you write <code>await</code> inside an actor, ask yourself: <em>"What assumptions have I made about state before this await that I need to re-verify after?"</em></p>
</li>
<li><p><strong>Choose your strategy</strong> based on your use case: correctness vs. throughput vs. simplicity.</p>
</li>
<li><p><strong>Test concurrent access explicitly</strong>—create stress tests that hammer your actors from multiple tasks simultaneously.</p>
</li>
</ol>
<p>The next time you write an actor method with an <code>await</code>, pause and ask yourself: "What could change while I'm suspended?" Your future self will thank you.</p>
<hr />
<p><em>For more on this topic, I highly recommend</em> <a target="_blank" href="https://www.donnywals.com/actor-reentrancy-in-swift-explained/"><em>Donny Wals' deep dive on actor re-entrancy</em></a><em>, which includes practical examples like building token refresh flows and async image loaders.</em></p>
<p><em>What patterns have you found useful for managing actor re-entrancy? Share your experiences in the comments!</em></p>
<p>#Swift #iOSDevelopment #Concurrency #SwiftConcurrency #Programming #SoftwareEngineering</p>
]]></content:encoded></item><item><title><![CDATA[Massive View Controllers and SwiftUI]]></title><description><![CDATA[Commenting on this post on LinkedIn: https://www.linkedin.com/posts/jacobmartinbartlett_the-biggest-problem-with-swiftui-is-that-activity-7261769674034409472-qGVe?utm_source=share&utm_medium=member_desktop
I replied with:

*The "Massive View Controll...]]></description><link>https://blog.encoded.life/massive-view-controllers-and-swiftui</link><guid isPermaLink="true">https://blog.encoded.life/massive-view-controllers-and-swiftui</guid><category><![CDATA[SwiftUI]]></category><category><![CDATA[MVC architecture]]></category><category><![CDATA[iOS]]></category><dc:creator><![CDATA[Dave Poirier]]></dc:creator><pubDate>Thu, 14 Nov 2024 01:52:30 GMT</pubDate><content:encoded><![CDATA[<p>Commenting on this post on LinkedIn: <a target="_blank" href="https://www.linkedin.com/posts/jacobmartinbartlett_the-biggest-problem-with-swiftui-is-that-activity-7261769674034409472-qGVe?utm_source=share&amp;utm_medium=member_desktop">https://www.linkedin.com/posts/jacobmartinbartlett_the-biggest-problem-with-swiftui-is-that-activity-7261769674034409472-qGVe?utm_source=share&amp;utm_medium=member_desktop</a></p>
<p>I replied with:</p>
<blockquote>
<p>*The "Massive View Controller" was never a problem if you understood that the "Model" of MVC was supposed to include all business logic.  </p>
<p>The issue occurs because people keep only data modelling, without any logic in the "Model" layer and move all business logic into the "Controller" layer.  </p>
<p>Ofcourse if you do the same in SwiftUI you are going to end up with Massive SwiftUI Views.  </p>
<p>Your app logic and business should be agnostic, as much of possible, from the UI layer. You should be able to swap your UIKit interface for a SwiftUI interface, or a command line interface, with minimum changes to your app.  </p>
<p>If you aren't able to do that, it's not UIKit or SwiftUI the issue, but your own understanding of how it's supposed to work. We don't need to invent MVVM, TCA, VIPER or any other complex architecture to handle that properly.*</p>
</blockquote>
<hr />
<h2 id="heading-asking-chatgpt">Asking ChatGPT:</h2>
<p>If you ask ChatGPT, it comes up with the following definition for the Model of the MVC on Apple platforms:</p>
<blockquote>
<p>In Apple's implementation of the Model-View-Controller (MVC) design pattern, the <strong>Model</strong> component is responsible for managing the application's data and business logic. It encapsulates the core information and defines the rules for manipulating that data. This includes tasks such as data persistence, networking, and data parsing. The Model operates independently of the user interface, ensuring that changes in the data do not directly affect the presentation layer. This separation promotes modularity and reusability within the application architecture.</p>
</blockquote>
<p>In this old document from Apple: <a target="_blank" href="https://developer.apple.com/library/archive/documentation/General/Conceptual/DevPedia-CocoaCore/MVC.html">https://developer.apple.com/library/archive/documentation/General/Conceptual/DevPedia-CocoaCore/MVC.html</a></p>
<h2 id="heading-apples-way">Apple’s way:</h2>
<p>We can find the following:</p>
<blockquote>
<h2 id="heading-model-objects"><strong>Model Objects</strong></h2>
<p>Model objects encapsulate the data specific to an application and define the logic and computation that manipulate and process that data. For example, a model object might represent a character in a game or a contact in an address book. A model object can have to-one and to-many relationships with other model objects, and so sometimes the model layer of an application effectively is one or more object graphs. Much of the data that is part of the persistent state of the application (whether that persistent state is stored in files or databases) should reside in the model objects after the data is loaded into the application. Because model objects represent knowledge and expertise related to a specific problem domain, they can be reused in similar problem domains. Ideally, a model object should have no explicit connection to the view objects that present its data and allow users to edit that data—it should not be concerned with user-interface and presentation issues.</p>
<p><strong>Communication</strong>: User actions in the view layer that create or modify data are communicated through a controller object and result in the creation or updating of a model object. When a model object changes (for example, new data is received over a network connection), it notifies a controller object, which updates the appropriate view objects.</p>
</blockquote>
<p>Looking at this definition, I can see why there might be confusion. It's not very clear where the object gets modified. There's a mention of "User actions" that are somehow communicated through a controller object, leading to the "updating of a model object"... but where is the code that updates the model object? Is it in the model, the controller, or maybe in the user action itself?</p>
<p>Considering what we know about the "Massive View Controller" problem, putting the code to modify the model object in the controller will likely make the controller objects too complex.</p>
<p>So if we are to understand the intent, it would be:</p>
<ul>
<li><p>to create a struct or object that represents the User Action itself</p>
</li>
<li><p>forward the User Action object via the controller to the model</p>
</li>
<li><p>model processes the User Action and creates or updates the model objects.</p>
</li>
<li><p>model updates notifies the controller, which then updates the view</p>
</li>
</ul>
<hr />
<h2 id="heading-what-lead-to-massive-view-controller-then">What lead to Massive View Controller then?</h2>
<p>Based on my experience, developers under used Views. Very often colors, fonts, spacing are all implemented in the controllers. Mistake number 1.</p>
<p>The MVC, intended for views to implement all aspects of their designs. This particular quote from Apple is telling:</p>
<blockquote>
<p>A view object knows how to draw itself and can respond to user actions.</p>
</blockquote>
<p>I have seldomly, if ever, seen a custom UIView in a project handling user actions. Usually you would have the view expose some IBActions and have the controller setup observers and handlers. Based on Apple’s documentation, if a view has a login form, it should forward to the controller a SignIn user action that contains the username and password. However, developers would typically connect the “Sign In” IBAction, and from the controller they would manually fetch the username and passwords from the textfields, etc. Mistake number 2.</p>
<p>As previously discussed, the model was intended to have all the model object creation and updates mechanisms. It’s fairly typical to have the controller perform the modifications based on the setup IBAction handler. Whereas it was originally intended to only forward the User Action to the model. Mistake number 3.</p>
<p>With this trifecta of mistakes, the controller keeps having more and more responsibility, which lead to developers coming up with a “ViewModel” (in MVVM) to play what was originally intended to be the controller’s role. Or in VIPER the Interactor and Presenter.</p>
<hr />
<h2 id="heading-effects-for-swiftui">Effects for SwiftUI</h2>
<p>In a system where MVC is developed as intended by Apple, SwiftUI takes on the role of the View and Controller all in one. Since the controller was mostly intended in converting the Model data into View data, and forwarding User Actions (not implementing the processing), removing the controller has the following impact:  </p>
<ul>
<li>SwiftUI is set to receive Model data updates, and send User Action to the Model layer.</li>
</ul>
<p>Would this lead to Massive SwiftUI views? No. The end result is MV - Model-View, and your View should be relatively lightweight.</p>
]]></content:encoded></item><item><title><![CDATA[Safely distributing API keys in your mobile app]]></title><description><![CDATA[The safest way is to keep your API keys server side. But if you have a library that's useable offline and it requires a license key to unlock, you will need a way to securely store that license key at runtime, without access to a server. Strap your h...]]></description><link>https://blog.encoded.life/safely-distributing-api-keys-in-your-mobile-app</link><guid isPermaLink="true">https://blog.encoded.life/safely-distributing-api-keys-in-your-mobile-app</guid><category><![CDATA[iOS]]></category><category><![CDATA[Mobile Development]]></category><category><![CDATA[appsec]]></category><category><![CDATA[Swift]]></category><category><![CDATA[secure coding]]></category><category><![CDATA[encryption]]></category><dc:creator><![CDATA[Dave Poirier]]></dc:creator><pubDate>Wed, 10 Jan 2024 03:51:50 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/PxiAc1aElFQ/upload/bef009eb4bae7b288a7a72d2b3be08b5.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>The safest way is to keep your API keys server side. But if you have a library that's useable offline and it requires a license key to unlock, you will need a way to securely store that license key at runtime, without access to a server. Strap your hat, you are going for a ride.</p>
<p>First, what not to do:</p>
<ul>
<li><p>Don't store your license/API key as a string in your source code</p>
</li>
<li><p>Don't store your license/API key as a value in Info.plist</p>
</li>
<li><p>Don't store your license/API key in a .xcconfig file that is used to populate your Info.plist</p>
</li>
<li><p>Don't store your license/API key in an environment variable used to populate your source code at compile time which will just store the value as a string in your source code.</p>
</li>
<li><p>Don't base64/base32 encode your key and think it's safe</p>
</li>
<li><p>Don't XOR your key.</p>
</li>
</ul>
<p>Yikes, so.. with all that, what can we do then?</p>
<p>You will want to use a cryptographically proven algorithm, like AES.GCM to encrypt the key at compile time, then use AES.GCM to decrypt the key at runtime.</p>
<p>I hear you, AES.GCM requires an encryption key, so how will you store the key? And that, my friends, is how the ride starts.</p>
<h1 id="heading-lets-ride">Let's ride.</h1>
<h2 id="heading-pick-a-password">Pick a password</h2>
<p>Come up with a good password. If you are not sure, <code>uuidgen</code> command in terminal is a relatively safe bet. Plus it won't look like a password so, bonus point for confusing your attackers.</p>
<h2 id="heading-pick-a-shared-secret">Pick a shared secret</h2>
<p>Next, at some point you will need a "shared secret" to derive your key. Hit that uuidgen command once more.</p>
<h2 id="heading-pick-an-obfuscator">Pick an obfuscator</h2>
<p>Grab an obfuscator library of your choice. SwiftSecretKeys (<a target="_blank" href="https://github.com/MatheusMBispo/SwiftSecretKeys">https://github.com/MatheusMBispo/SwiftSecretKeys</a>) seems decent enough, there are others. Most of them are not cryptographically safe but that's okay. It will be better than storing your password and shared secret as strings in your source code.</p>
<p>Make sure you understand how to use the chosen obfuscator. Store your password and shared secret in there. Please give them creative names. If your obfuscator works with string keys, go ahead and generate two more UUID to use as keys.</p>
<pre><code class="lang-yaml"><span class="hljs-attr">output:</span> <span class="hljs-string">Generated/</span>
<span class="hljs-attr">keys:</span>
  <span class="hljs-attr">kE2110F7359B34F6EB11AC01F96652F44:</span> <span class="hljs-string">'77783393-62DE-499E-8848-68FF3D4A2979'</span>
  <span class="hljs-attr">k3A0535981E94408E86BAB3CFE466C399:</span> <span class="hljs-string">'849C3BB6-2519-4BF4-8E7A-3E6D7D9C003B'</span>
</code></pre>
<pre><code class="lang-swift"><span class="hljs-keyword">let</span> mySharedSecret = <span class="hljs-type">SecretKeys</span>.kE2110F7359B34F6EB11AC01F96652F44
<span class="hljs-keyword">let</span> myPassword = <span class="hljs-type">SecretKeys</span>.k3A0535981E94408E86BAB3CFE466C399
</code></pre>
<p>Assuming your stuff is setup right, this should give you a mySharedSecret variable that contains the string "77783393-62DE-499E-8848-68FF3D4A2979" and myPassword that contains the string "849C3BB6-2519-4BF4-8E7A-3E6D7D9C003B"</p>
<h2 id="heading-learn-how-to-use-hkdf">Learn how to use HKDF</h2>
<p>Apple's doc on HKDF is at <a target="_blank" href="https://developer.apple.com/documentation/cryptokit/hkdf">https://developer.apple.com/documentation/cryptokit/hkdf</a></p>
<p>In short, HDKF is a key derivation algorithm, you give it a password, a salt, and tell it how many bytes of data you need for the key, and it's going to do its magic until there's enough data to fill the key. In our case we will be using AES.GCM with 256-bit keys. Given 8-bits per byte, we will need 32 bytes.</p>
<pre><code class="lang-swift"><span class="hljs-keyword">let</span> passwordData = myPassword.data(using: .utf8)!
<span class="hljs-keyword">let</span> sharedSecretData = mySharedSecret.data(using: .utf8)!
<span class="hljs-keyword">let</span> aesKey = <span class="hljs-type">HKDF</span>&lt;<span class="hljs-type">SHA512</span>&gt;.deriveKey(
            inputKeyMaterial: passwordData,
            info: sharedSecretData,
            outputByteCount: <span class="hljs-number">32</span>)
</code></pre>
<h2 id="heading-learn-how-to-use-aesgcm">Learn how to use AES.GCM</h2>
<p>Apple's doc on AES.GCM is at <a target="_blank" href="https://developer.apple.com/documentation/cryptokit/aes/gcm">https://developer.apple.com/documentation/cryptokit/aes/gcm</a></p>
<p>AES is the encryption algorithm, GCM is the block cipher which also includes some AEAD authentication data at the end. No need to go into too much details here, it allows you to encrypt some plain text data into gibberish unintelligible data commonly known as cipher text.</p>
<p>plain text = stuff not encrypted</p>
<p>cipher text = gibberish</p>
<p>AES.GCM uses something called a SealedBox to store the cipher text. So when you want to encrypt your plain text, you use AES.GCM.seal() to create a SealedBox, and when you want to decrypt your cipher text back to plain text you first create a SealedBox using the cipher text, and you open the SealedBox using AES.GCM.open().</p>
<p>Creating the [UInt8] sequence to include in your source code, assuming you already have the HKDF to generate the <code>aesKey</code>:</p>
<pre><code class="lang-swift"><span class="hljs-keyword">let</span> myApiKey = <span class="hljs-string">"I love Apple"</span>
<span class="hljs-keyword">let</span> myApiKeyPlainText: <span class="hljs-type">Data</span> = myApiKey.data(using: .utf8)!
<span class="hljs-keyword">let</span> sealedBox = <span class="hljs-keyword">try</span>! <span class="hljs-type">AES</span>.<span class="hljs-type">GCM</span>.seal(myApiKeyPlainText, using: aesKey)
<span class="hljs-keyword">let</span> myApiKeyCipherText: <span class="hljs-type">Data</span> = sealedBox.combined!
<span class="hljs-keyword">let</span> cipherRepresentation = myApiKeyCipherText.<span class="hljs-built_in">map</span> { <span class="hljs-type">String</span>(format: <span class="hljs-string">"0x%02X"</span>, $<span class="hljs-number">0</span> }.joined(separator: <span class="hljs-string">","</span>)
<span class="hljs-built_in">print</span>(<span class="hljs-string">"private let apiKeyCipherText = [\(cipherRepresentation)]"</span>)

<span class="hljs-comment">// will print:</span>
<span class="hljs-comment">// private let apiKeyCipherText: [UInt8] = [0xA2,0xC3,0xC7,0x56,0x66,0x28,0x73,0x87,0x1B,0xC6,0xF8,0x64,0x8D,0x8F,0x80,0x2C,0xB0,0xF2,0x87,0x57,0xFF,0x87,0x75,0x74,0x03,0x7B,0x92,0xD6,0xFC,0xF7,0xDF,0xB9,0xD6,0xBA,0x80,0xB9,0x1D,0xCD,0x6E,0xAB]</span>
</code></pre>
<p>In your source code, assuming you already have the HKDF to generate the <code>aesKey</code>:</p>
<pre><code class="lang-swift"> <span class="hljs-keyword">private</span> <span class="hljs-keyword">let</span> apiKeyCipherText: [<span class="hljs-type">UInt8</span>] = [<span class="hljs-number">0xA2</span>,<span class="hljs-number">0xC3</span>,<span class="hljs-number">0xC7</span>,<span class="hljs-number">0x56</span>,<span class="hljs-number">0x66</span>,<span class="hljs-number">0x28</span>,<span class="hljs-number">0x73</span>,<span class="hljs-number">0x87</span>,<span class="hljs-number">0x1B</span>,<span class="hljs-number">0xC6</span>,<span class="hljs-number">0xF8</span>,<span class="hljs-number">0x64</span>,<span class="hljs-number">0x8D</span>,<span class="hljs-number">0x8F</span>,<span class="hljs-number">0x80</span>,<span class="hljs-number">0x2C</span>,<span class="hljs-number">0xB0</span>,<span class="hljs-number">0xF2</span>,<span class="hljs-number">0x87</span>,<span class="hljs-number">0x57</span>,<span class="hljs-number">0xFF</span>,<span class="hljs-number">0x87</span>,<span class="hljs-number">0x75</span>,<span class="hljs-number">0x74</span>,<span class="hljs-number">0x03</span>,<span class="hljs-number">0x7B</span>,<span class="hljs-number">0x92</span>,<span class="hljs-number">0xD6</span>,<span class="hljs-number">0xFC</span>,<span class="hljs-number">0xF7</span>,<span class="hljs-number">0xDF</span>,<span class="hljs-number">0xB9</span>,<span class="hljs-number">0xD6</span>,<span class="hljs-number">0xBA</span>,<span class="hljs-number">0x80</span>,<span class="hljs-number">0xB9</span>,<span class="hljs-number">0x1D</span>,<span class="hljs-number">0xCD</span>,<span class="hljs-number">0x6E</span>,<span class="hljs-number">0xAB</span>]
 <span class="hljs-keyword">let</span> sealedBox = <span class="hljs-keyword">try</span>! <span class="hljs-type">AES</span>.<span class="hljs-type">GCM</span>.<span class="hljs-type">SealedBox</span>(combined: <span class="hljs-type">Data</span>(apiKeyCipherText))
 <span class="hljs-keyword">let</span> myApiKeyPlainText = <span class="hljs-keyword">try</span>! <span class="hljs-type">AES</span>.<span class="hljs-type">GCM</span>.<span class="hljs-keyword">open</span>(sealedBox, using: aesKey)
 <span class="hljs-keyword">let</span> myApiKey = <span class="hljs-type">String</span>(data: myAPiKeyPlainText, encoding: .utf8)!
 <span class="hljs-built_in">print</span>(myApiKey)

 <span class="hljs-comment">// will print</span>
 <span class="hljs-comment">// I love Apple</span>
</code></pre>
<h1 id="heading-congrats">Congrats!</h1>
<p>You can now securely store license keys and secrets, in your mobile app.</p>
<h2 id="heading-you-are-not-done">You are not done.</h2>
<p>If you went through all this trouble just to send your API key over some network request using HTTPS, your API key will be easily accessible to hackers using TLS bypass attacks.</p>
<p>You can learn more about this by searching for Radare2, Frida or Objection. You can detect TLS bypass attacks but you can't really prevent them. So your API key will be exposed the moment you send it over the network, unless you encrypt before sending it over to the server.</p>
<p>Most online services expect you to send your API key as is, so most of your hard work to keep your API key secret will, well, be for naught.</p>
<p>But, if your API key is used locally in a commercial library, then you are all set, or almost.</p>
<p>You will want to finish protecting your application with jailbreak detection, debugger detection, etc.</p>
<p>Happy coding!</p>
]]></content:encoded></item><item><title><![CDATA[Pick [static|dynamic|.xcframework] ?]]></title><description><![CDATA[Someone asked me that question earlier. Well, as with everything else, it depends.  
First, if your module/library will need to be distributed with assets, you should go with a .xcframework as it can include additional files. Manually distributing a ...]]></description><link>https://blog.encoded.life/pick-staticdynamicxcframework</link><guid isPermaLink="true">https://blog.encoded.life/pick-staticdynamicxcframework</guid><category><![CDATA[iOS]]></category><category><![CDATA[Swift]]></category><category><![CDATA[Mobile Development]]></category><dc:creator><![CDATA[Dave Poirier]]></dc:creator><pubDate>Mon, 08 Jan 2024 21:07:59 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/zjV8ptYgcEo/upload/b7f2544deaa1d14c4ce4aba907672703.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Someone asked me that question earlier. Well, as with everything else, it depends.  </p>
<p>First, if your module/library will need to be distributed with assets, you should go with a .xcframework as it can include additional files. Manually distributing a .a or .dyld file along with a set of assets to include is a recipe for disaster. So you'll pick .xcframework; and in that framework will be your static and/or dynamic library.  </p>
<p>As for static vs dynamic, it depends on how your app will be using it. If it will always be loaded and your app requires that functionality to work, then build it as a static library. Your main app executable will be slightly larger but still smaller than app binary + dynamic library. And since you will always need to load the dynamic library, you will also save some time because it will already be linked at compile time. So... faster for your users.  </p>
<p>However, if your library contains a bunch of stuff that's mostly needed only for specific edge cases in your app, then you will want to pick a dynamic library. This will allow your app to start immediately, and iOS will load the dynamic library as needed at runtime.  </p>
<p>Note that using a dynamic library may also introduce some unexpected delays due to the dynamic nature of the linking. So be careful about making use of a function from a dynamic library for the first time during animations.</p>
]]></content:encoded></item><item><title><![CDATA[The importance of `final` for security]]></title><description><![CDATA[When creating a Swift class or actor, if you don't intend to override any of the functions in that class you should use the final keyword. Here's an example of why this is important:

Notice that the attacker did not even need to match the function n...]]></description><link>https://blog.encoded.life/the-importance-of-final-for-security</link><guid isPermaLink="true">https://blog.encoded.life/the-importance-of-final-for-security</guid><category><![CDATA[iOS]]></category><category><![CDATA[Swift]]></category><category><![CDATA[appsec]]></category><dc:creator><![CDATA[Dave Poirier]]></dc:creator><pubDate>Sun, 07 Jan 2024 02:37:17 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/flha0KwRrRc/upload/50b93f5a20af90342f20b7edde48121b.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>When creating a Swift class or actor, if you don't intend to override any of the functions in that class you should use the <code>final</code> keyword. Here's an example of why this is important:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1704594618308/08ebfe9a-fffb-4b98-84ba-0168bb5119b9.png" alt class="image--center mx-auto" /></p>
<p>Notice that the attacker did not even need to match the function name. Swift uses a dynamic lookup table for non-final classes to identify where the implementation of a function is located.</p>
<p>Since world() is the first function of the Hello class, and notSoFast() is the first function of the Hack class, when the <code>hello</code> instance type is changed to Hack and we call the world() function on it, underneath Swift will retrieve the first function implementation of the dynamic lookup table of the Hack class, and end up executing notSoFast() instead of world().</p>
<p>When you use final(), Swift will directly refer to the implementation of world() and skip the dynamic lookup table. Making your code safer from attackers.</p>
<hr />
<p>Edit: As pointed out by several in the community, this "trick" is mostly prevented by Swift Whole Module optimization (-wmo or -whole-module-optimization). Provided your libraries and your application have the right build settings, the role of <code>final</code> keyword is mostly to prevent other developers from extending your classes.  </p>
<p>I greatly appreciate the interest this generated in the community. And hopefully this allowed many of you to learn more about Swift dynamic lookup and class handling.  </p>
<p>You can read more about Whole Module Optimization at <a target="_blank" href="https://www.swift.org/blog/whole-module-optimizations/">https://www.swift.org/blog/whole-module-optimizations/</a></p>
]]></content:encoded></item><item><title><![CDATA[Your Swift app is full of symbols, even if stripped.]]></title><description><![CDATA[We have been drilled that we should strip debugging symbols before releasing our app on the App Store. Apple even does this automatically for us in the build settings when we create a new project.
However when we are using Swift as our programming la...]]></description><link>https://blog.encoded.life/your-swift-app-is-full-of-symbols-even-if-stripped</link><guid isPermaLink="true">https://blog.encoded.life/your-swift-app-is-full-of-symbols-even-if-stripped</guid><category><![CDATA[iOS]]></category><category><![CDATA[macOS]]></category><category><![CDATA[appsec]]></category><category><![CDATA[mobile security]]></category><category><![CDATA[Swift]]></category><dc:creator><![CDATA[Dave Poirier]]></dc:creator><pubDate>Sat, 06 Jan 2024 02:08:49 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1704506845557/24066c25-6483-4fff-9586-5e178e4820c3.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>We have been drilled that we should strip debugging symbols before releasing our app on the App Store. Apple even does this automatically for us in the build settings when we create a new project.</p>
<p>However when we are using Swift as our programming language, a certain feature called Reflection, embeds in the app binary all the necessary names and types of every class/struct you have defined in your project so you can Mirror() the object and list all the attributes/types dynamically at runtime.</p>
<p>This trove of information is an incredible source of information to hackers. Apparently, this can be controlled by a Swift compiler flag <code>SWIFT_REFLECTION_METADATA_LEVEL</code>.</p>
<p>I stumbled on this in an Apple Developer Forum post: <a target="_blank" href="https://developer.apple.com/forums/thread/720978">https://developer.apple.com/forums/thread/720978</a></p>
<p>Which lead me to this Apple Documentation page: <a target="_blank" href="https://developer.apple.com/documentation/xcode/build-settings-reference#Reflection-Metadata-Level">https://developer.apple.com/documentation/xcode/build-settings-reference#Reflection-Metadata-Level</a></p>
<p>So if you are securing an iOS/macOS app, make sure to go into your Xcode project build settings, and find <code>Swift Compiler General</code> -&gt; <code>Reflection Metadata Level</code> and set the value to <code>None</code>.</p>
]]></content:encoded></item><item><title><![CDATA[Typealias: Swift's underestimated champion]]></title><description><![CDATA[I've been aware of typealias for a long time. It's only recently that I really started to understand just how powerful this keyword can be. Let me share why I think they are the underestimated champion for unit tests and dependency injection.
Referen...]]></description><link>https://blog.encoded.life/typealias-swifts-underestimated-champion</link><guid isPermaLink="true">https://blog.encoded.life/typealias-swifts-underestimated-champion</guid><category><![CDATA[Swift]]></category><category><![CDATA[clean code]]></category><category><![CDATA[unit testing]]></category><dc:creator><![CDATA[Dave Poirier]]></dc:creator><pubDate>Sat, 23 Dec 2023 19:41:46 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/ew3-7k3sl-g/upload/6d941116ced381e0a55707e4c760c346.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I've been aware of <code>typealias</code> for a long time. It's only recently that I really started to understand just how powerful this keyword can be. Let me share why I think they are the underestimated champion for unit tests and dependency injection.</p>
<h1 id="heading-reference-use-case">Reference Use Case</h1>
<p>A class depends on the services of another class. Your first instinct will likely be to create a protocol that will define the interface that you will require.</p>
<pre><code class="lang-swift"><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">GroceryList</span>: <span class="hljs-title">Codable</span> </span>{
    <span class="hljs-keyword">var</span> items: [<span class="hljs-type">String</span>]
}

<span class="hljs-class"><span class="hljs-keyword">protocol</span> <span class="hljs-title">GroceryListLoader</span>() </span>{
    <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">loadFromURL</span><span class="hljs-params">(<span class="hljs-number">_</span> url: URL)</span></span> <span class="hljs-keyword">throws</span> -&gt; <span class="hljs-type">GroceryList</span>
}

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MyGroceryListManager</span> </span>{
    <span class="hljs-keyword">let</span> loader: <span class="hljs-type">GroceryListLoader</span>
    <span class="hljs-keyword">var</span> groceryLists = [<span class="hljs-type">Name</span>: <span class="hljs-type">GroceryList</span>]()

    <span class="hljs-keyword">init</span>(loader: <span class="hljs-type">GroceryListLoader</span>) {
        <span class="hljs-keyword">self</span>.loader = loader
    }

    <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">openList</span><span class="hljs-params">(name: String)</span></span> <span class="hljs-keyword">throws</span> {
        <span class="hljs-keyword">let</span> listUrl = <span class="hljs-type">URL</span>(string: <span class="hljs-string">"file://path/to/\(name).json"</span>)!
        groceryLists[name] = <span class="hljs-keyword">try</span> loader.loadFromUrl(listUrl)
    }
}
</code></pre>
<p>This allows you to replace the implementation of the GroceryListLoader with a mock/stub during unit tests. It also makes your code more flexible as it can work with any number of implementations as long as they can conform to the GroceryListLoader protocol.</p>
<h2 id="heading-reference-case-unit-test">Reference Case Unit Test</h2>
<p>Let's create a small unit test to demonstrate how this is typically implemented:</p>
<pre><code class="lang-swift"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">GroceryListLoaderMock</span>: <span class="hljs-title">GroceryListLoader</span> </span>{
    <span class="hljs-class"><span class="hljs-keyword">enum</span> <span class="hljs-title">Errors</span>: <span class="hljs-title">Error</span> </span>{
      <span class="hljs-keyword">case</span> fileNotFound
    }

    <span class="hljs-keyword">var</span> lists = [<span class="hljs-type">URL</span>: <span class="hljs-type">GroceryList</span>]()
    <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">loadFromURL</span><span class="hljs-params">(<span class="hljs-number">_</span> url: URL)</span></span> <span class="hljs-keyword">throws</span> -&gt; <span class="hljs-type">GroceryList</span> {
        <span class="hljs-keyword">guard</span> <span class="hljs-keyword">let</span> list = lists[url] <span class="hljs-keyword">else</span> {
            <span class="hljs-keyword">throw</span> <span class="hljs-type">Errors</span>.fileNotFound
        }
        <span class="hljs-keyword">return</span> list
    }
}

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">GroceryListManagerTests</span>: <span class="hljs-title">XCTestCase</span> </span>{
    <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">testThrowsWhenFileIsMissing</span><span class="hljs-params">()</span></span> {
        <span class="hljs-keyword">let</span> mockLoader = <span class="hljs-type">GroceryListLoaderMock</span>()
        <span class="hljs-keyword">let</span> manager = <span class="hljs-type">GroceryListManager</span>(loader: mockLoader)
        <span class="hljs-type">XCTAssertThrow</span>(<span class="hljs-keyword">try</span> manager.openList(name: <span class="hljs-string">"Walmart"</span>))
    }
}
</code></pre>
<p>Nothing unusual here. When executing <code>testThrowsWhenFileIsMissing()</code> a mockLoader is created, and since no list was assigned to the mock's lists, the <code>loadFromURL()</code> function will throw the <code>Errors.fileNotFound</code>. In turn the <code>GroceryListManager</code> implementation of <code>openList()</code> because it doesn't capture any error throw by the loader, will simply allow the thrown error to carry on to the caller of the <code>openList()</code> function therefore passing the test.</p>
<h1 id="heading-typealias-alternative">Typealias alternative</h1>
<pre><code class="lang-swift"><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">GroceryList</span>: <span class="hljs-title">Codable</span> </span>{
    <span class="hljs-keyword">var</span> items: [<span class="hljs-type">String</span>]
}

<span class="hljs-keyword">typealias</span> <span class="hljs-type">LoadGroceryList</span> = (<span class="hljs-number">_</span> url: <span class="hljs-type">URL</span>) <span class="hljs-keyword">throws</span> -&gt; <span class="hljs-type">GroceryList</span>

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">GroceryListManager</span> </span>{
    <span class="hljs-keyword">let</span> loader: <span class="hljs-type">LoadGroceryList</span>
    <span class="hljs-keyword">var</span> groceryLists = [<span class="hljs-type">Name</span>: <span class="hljs-type">GroceryList</span>]()

    <span class="hljs-keyword">init</span>(loader: @escaping <span class="hljs-type">LoadGroceryList</span>) {
        <span class="hljs-keyword">self</span>.loader = loader
    }

    <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">openList</span><span class="hljs-params">(name: String)</span></span> <span class="hljs-keyword">throws</span> {
        <span class="hljs-keyword">let</span> listUrl = <span class="hljs-type">URL</span>(string: <span class="hljs-string">"file://path/to/\(name).json"</span>)!
        groceryLists[name] = <span class="hljs-keyword">try</span> loader(listUrl)
    }
}
</code></pre>
<p>Here, instead of defining a protocol, we define a <code>typealias</code> with the signature of the closure we need. In the <code>GroceryListManager</code> we will store a reference to that closure, ensuring we tag it as <code>@escaping</code> since it will be used outside the scope of <code>init()</code>. Finally, in <code>openList()</code> we execute the closure to receive the list at the given URL.</p>
<h2 id="heading-typealias-unit-test">Typealias Unit Test</h2>
<pre><code class="lang-swift"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">GroceryListManagerTests</span>: <span class="hljs-title">XCTestCase</span> </span>{
    <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">testThrowsWhenFileIsMissing</span><span class="hljs-params">()</span></span> {
        <span class="hljs-class"><span class="hljs-keyword">enum</span> <span class="hljs-title">Errors</span>: <span class="hljs-title">Error</span> </span>{
           <span class="hljs-keyword">case</span> fileNotFound
        }
        <span class="hljs-keyword">let</span> mockLoader: <span class="hljs-type">LoadGroceryList</span> = { <span class="hljs-number">_</span> <span class="hljs-keyword">in</span> <span class="hljs-keyword">throw</span> <span class="hljs-type">Errors</span>.fileNotFound }
        <span class="hljs-keyword">let</span> manager = <span class="hljs-type">GroceryListManager</span>(loader: mockLoader)
        <span class="hljs-type">XCTAssertThrow</span>(<span class="hljs-keyword">try</span> manager.openList(name: <span class="hljs-string">"Walmart"</span>))
    }
}
</code></pre>
<p>Because the <code>typealias</code> defines the closure signature needed to fulfill the requirements needed by <code>GroceryListManager</code> class, we no longer need a mock class to be created. We can directly implement a custom implementation needed in the closure.</p>
<p>It could even be simplified to the following:</p>
<pre><code class="lang-swift"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">GroceryListManagerTests</span>: <span class="hljs-title">XCTestCase</span> </span>{
    <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">testThrowsWhenFileIsMissing</span><span class="hljs-params">()</span></span> {
        <span class="hljs-class"><span class="hljs-keyword">enum</span> <span class="hljs-title">Errors</span>: <span class="hljs-title">Error</span> </span>{
           <span class="hljs-keyword">case</span> fileNotFound
        }
        <span class="hljs-keyword">let</span> manager = <span class="hljs-type">GroceryListManager</span>(
            loader: { <span class="hljs-number">_</span> <span class="hljs-keyword">in</span> <span class="hljs-keyword">throw</span> <span class="hljs-type">Errors</span>.fileNotFound })
        <span class="hljs-type">XCTAssertThrow</span>(<span class="hljs-keyword">try</span> manager.openList(name: <span class="hljs-string">"Walmart"</span>))
    }
}
</code></pre>
<p>As you can see, the unit test is much easier to comprehend, as we no longer depend on the specific implementation of the Mock.</p>
<h1 id="heading-declaring-conformance-in-another-class">Declaring conformance in another class</h1>
<p>In reality, many of your classes may expose multiple functions. You can still be empowered to use <code>typealias</code>, for example:</p>
<pre><code class="lang-swift"><span class="hljs-keyword">typealias</span> <span class="hljs-type">LoadGroceryList</span> = (<span class="hljs-type">URL</span>) <span class="hljs-keyword">throws</span> -&gt; <span class="hljs-type">GroceryList</span>

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MyGroceryListLoader</span> </span>{
   <span class="hljs-keyword">let</span> loadFromUrl: <span class="hljs-type">LoadGroceryList</span> = loadGroceryList

   <span class="hljs-keyword">private</span> <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">loadGroceryList</span><span class="hljs-params">(<span class="hljs-number">_</span> url: URL)</span></span> <span class="hljs-keyword">throws</span> -&gt; <span class="hljs-type">GroceryList</span> {
     ...
   }
}
</code></pre>
<p>You can also define protocol conformance like you used to if needed:</p>
<pre><code class="lang-swift"><span class="hljs-keyword">typealias</span> <span class="hljs-type">LoadGroceryList</span> = (<span class="hljs-type">URL</span>) <span class="hljs-keyword">throws</span> -&gt; <span class="hljs-type">GroceryList</span>
<span class="hljs-keyword">typealias</span> <span class="hljs-type">WriteGroceryList</span> = (<span class="hljs-type">GroceryList</span>, <span class="hljs-type">URL</span>) <span class="hljs-keyword">throws</span> -&gt; <span class="hljs-type">Void</span>

<span class="hljs-class"><span class="hljs-keyword">protocol</span> <span class="hljs-title">GroceryListLoaderWriter</span> </span>{
   <span class="hljs-keyword">var</span> loadFromURL: <span class="hljs-type">LoadGroceryList</span> { <span class="hljs-keyword">get</span> }
   <span class="hljs-keyword">var</span> writeToURL: <span class="hljs-type">WriteGroceryList</span> { <span class="hljs-keyword">get</span> }
}

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MyGroceryListLoaderWriter</span>: <span class="hljs-title">GroceryListLoaderWriter</span> </span>{
   <span class="hljs-keyword">let</span> loadFromURL: <span class="hljs-type">LoadGroceryList</span> = loadGroceryList
   <span class="hljs-keyword">let</span> writeToURL: <span class="hljs-type">WriteGroceryList</span> = writeGroceryList

   <span class="hljs-keyword">private</span> <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">loadGroceryList</span><span class="hljs-params">(<span class="hljs-number">_</span> url: URL)</span></span> <span class="hljs-keyword">throws</span> -&gt; <span class="hljs-type">GroceryList</span> {
      ...
   }

   <span class="hljs-keyword">private</span> <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">writeGroceryList</span><span class="hljs-params">(<span class="hljs-number">_</span> groceryList: GroceryList, <span class="hljs-number">_</span> url: URL)</span></span> <span class="hljs-keyword">throws</span> {
      ...
   }
}

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MyGroceryListManager</span> </span>{
   <span class="hljs-keyword">let</span> loader: <span class="hljs-type">GroceryListLoaderWriter</span>
   <span class="hljs-keyword">var</span> openedLists = [<span class="hljs-type">Name</span>: <span class="hljs-type">GroceryList</span>]()

   <span class="hljs-keyword">init</span>(loader: <span class="hljs-type">GroceryListLoaderWriter</span>) {
      <span class="hljs-keyword">self</span>.loader = loader
   }

   <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">openList</span><span class="hljs-params">(name: String)</span></span> <span class="hljs-keyword">throws</span> {
      <span class="hljs-keyword">let</span> listUrl = <span class="hljs-type">URL</span>(string: <span class="hljs-string">"/path/to/\(name).json"</span>)!
      openedLists[name] = <span class="hljs-keyword">try</span> loader.loadFromUrl(listUrl)
   }
}

<span class="hljs-keyword">let</span> myLoaderWriter = <span class="hljs-type">MyGroceryListLoaderWriter</span>()
<span class="hljs-keyword">let</span> myManager = <span class="hljs-type">MyGroceryListManager</span>(loader: myLoaderWriter)
</code></pre>
<p>In the above case however, we can notice that MyGroceryListManager doesn't use the writer, only the reader, so it can be simplified to:</p>
<pre><code class="lang-swift"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MyGroceryListManager</span> </span>{
   <span class="hljs-keyword">let</span> loader: <span class="hljs-type">LoadGroceryList</span>
   <span class="hljs-keyword">var</span> openedLists = [<span class="hljs-type">Name</span>: <span class="hljs-type">GroceryList</span>]()

   <span class="hljs-keyword">init</span>(loader: @escaping <span class="hljs-type">LoadGroceryList</span>) {
      <span class="hljs-keyword">self</span>.loader = loader
   }

   <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">openList</span><span class="hljs-params">(name: String)</span></span> <span class="hljs-keyword">throws</span> {
      <span class="hljs-keyword">let</span> listUrl = <span class="hljs-type">URL</span>(string: <span class="hljs-string">"/path/to/\(name).json"</span>)!
      openedLists[name] = <span class="hljs-keyword">try</span> loader(listUrl)
   }
}

<span class="hljs-keyword">let</span> myLoaderWriter = <span class="hljs-type">MyGroceryListLoaderWriter</span>()
<span class="hljs-keyword">let</span> myManager = <span class="hljs-type">MyGroceryListManager</span>(loader: myLoaderWriter.loadFromUrl)
</code></pre>
<h1 id="heading-conclusion">Conclusion</h1>
<p>Using <code>typealias</code> can provide you with additional flexibility and may end up requiring fewer lines of code in your app and in your unit tests.</p>
<p>If your class depends on the entirety of the protocol, then please continue to use your protocols like you are used to. However, if your class depends only on a subset of your protocol, a <code>typealias</code> may just be the tool that will allow you to simplify your unit tests and your dependency injections.</p>
]]></content:encoded></item><item><title><![CDATA[Secure JSON handling in Swift]]></title><description><![CDATA[When storing or reading data from a disk, or when making API calls, does your app's sole validation of data involve ensuring that the JSON can be decoded?
How do you receive email addresses in your JSON? Do you use a simple string variable?
There is ...]]></description><link>https://blog.encoded.life/secure-json-handling-in-swift</link><guid isPermaLink="true">https://blog.encoded.life/secure-json-handling-in-swift</guid><category><![CDATA[iOS]]></category><category><![CDATA[Swift]]></category><category><![CDATA[clean code]]></category><category><![CDATA[data structures]]></category><category><![CDATA[mobile security]]></category><dc:creator><![CDATA[Dave Poirier]]></dc:creator><pubDate>Tue, 14 Nov 2023 20:06:01 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1699988751419/8951dd97-bfb6-48b9-9693-f6469834756d.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>When storing or reading data from a disk, or when making API calls, does your app's sole validation of data involve ensuring that the JSON can be decoded?</p>
<p>How do you receive email addresses in your JSON? Do you use a simple string variable?</p>
<p>There is a high likelihood that your current JSON is not as secure as it could be. When combined with other vulnerabilities in libraries and operating systems, a wide range of undesirable outcomes, from crashes to compromised user devices, may arise due to insecure JSON handling.</p>
<hr />
<h1 id="heading-typical-client-struct"><strong>Typical “Client” struct</strong></h1>
<p>Let's envision a scenario where we need to obtain a list of prospective clients from an API, which includes their names, ages, and email addresses.</p>
<pre><code class="lang-json">[{<span class="hljs-attr">"name"</span>:<span class="hljs-string">"John Doe"</span>,<span class="hljs-attr">"age"</span>:<span class="hljs-number">35</span>,<span class="hljs-attr">"email"</span>:<span class="hljs-string">"john@doe.test"</span>}]
</code></pre>
<p>It would be typical to expect the following associated code:</p>
<pre><code class="lang-swift"><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">Client</span>: <span class="hljs-title">Codable</span> </span>{
    <span class="hljs-keyword">let</span> name: <span class="hljs-type">String</span>
    <span class="hljs-keyword">let</span> age: <span class="hljs-type">Int</span>
    <span class="hljs-keyword">let</span> email: <span class="hljs-type">String</span>
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">decodeClientList</span><span class="hljs-params">(data: Data)</span></span> <span class="hljs-keyword">throws</span> -&gt; [<span class="hljs-type">Client</span>] {
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">try</span> <span class="hljs-type">JSONDecoder</span>().decode([<span class="hljs-type">Client</span>].<span class="hljs-keyword">self</span>, from: data)
}
</code></pre>
<p>Unfortunately, this allows for various inputs that don't make sense in our context. For instance, does it make sense for a client's age to be 150,000 years old? Yet, your JSON permits it. Similarly, the email address could be set to "Hello" in the JSON, and the decoder wouldn't raise any issues.</p>
<p>You may need to perform validation on the Client data after it has been decoded. Additionally, remember to re-validate the data when retrieving it from CoreData later on or when accessing it through your caching layer.</p>
<p>Instead, you could declare your Codable in such a way that <strong>validation is performed while decoding</strong>.</p>
<hr />
<h1 id="heading-age"><strong>Age</strong></h1>
<p>Let’s begin by defining the minimum and maximum age for your clients.</p>
<ul>
<li><p>Any “age” value higher than 150 is likely a mistake and shouldn’t be allowed.</p>
</li>
<li><p>Negative values are clearly out of the question (you could use UInt..)</p>
</li>
<li><p>You might even want to set a minimum value, let’s go with 1 for now</p>
</li>
</ul>
<pre><code class="lang-swift"><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">Age</span>: <span class="hljs-title">Codable</span> </span>{
    <span class="hljs-keyword">let</span> value: <span class="hljs-type">Int</span>

    <span class="hljs-class"><span class="hljs-keyword">enum</span> <span class="hljs-title">Errors</span>: <span class="hljs-title">Error</span> </span>{
        <span class="hljs-keyword">case</span> outOfRange
    }

    <span class="hljs-keyword">static</span> <span class="hljs-keyword">let</span> minimumAge = <span class="hljs-number">1</span>
    <span class="hljs-keyword">static</span> <span class="hljs-keyword">let</span> maximumAge = <span class="hljs-number">150</span>

    <span class="hljs-meta">@discardableResult</span>
    <span class="hljs-keyword">static</span> <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">evaluated</span><span class="hljs-params">(<span class="hljs-number">_</span> age: Int)</span></span> <span class="hljs-keyword">throws</span> -&gt; <span class="hljs-type">Int</span> {
        <span class="hljs-keyword">guard</span> (minimumAge...maximumAge).<span class="hljs-built_in">contains</span>(age) <span class="hljs-keyword">else</span> {
            <span class="hljs-keyword">throw</span> <span class="hljs-type">Errors</span>.outOfRange
        }
        <span class="hljs-keyword">return</span> age
    }

    <span class="hljs-keyword">init</span>(from decoder: <span class="hljs-type">Decoder</span>) <span class="hljs-keyword">throws</span> {
        <span class="hljs-keyword">let</span> age = <span class="hljs-keyword">try</span> decoder
            .singleValueContainer()
            .decode(<span class="hljs-type">Int</span>.<span class="hljs-keyword">self</span>)
        <span class="hljs-keyword">self</span>.value = <span class="hljs-keyword">try</span> <span class="hljs-type">Age</span>.evaluated(age)
    }    
}
</code></pre>
<p>In the code above, we have created a struct that contains an integer value representing age, and we have defined the errors that will be thrown if the value does not meet our criteria. We then proceed to create a static function that evaluates the value against our criteria.</p>
<p>The purpose of using a static function for evaluation outside the decoder is that it can be reused in other initializers or anywhere within our app where we need to determine if a value meets the criteria.</p>
<p>Finally for the Age struct, we define the initializer, that extracts the value, evaluates and stores it.</p>
<hr />
<h1 id="heading-name"><strong>Name</strong></h1>
<p>After taking some time to reflect on client names, I realize that I'm not aware of anyone having a name longer than 100 characters, and certainly no one with a name exceeding 500 characters. It's also safe to assume that no one has an "@" symbol or a backslash in their name. Defining some rules, we obtain:</p>
<ul>
<li><p>Can contain alphabetical characters (from various alphabets and languages)</p>
</li>
<li><p>Can contain spaces but only between the names</p>
</li>
<li><p>Should contain at least 2 characters</p>
</li>
<li><p>Should be no longer than 100</p>
</li>
</ul>
<pre><code class="lang-swift"><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">Name</span>: <span class="hljs-title">Codable</span> </span>{
    <span class="hljs-keyword">let</span> value: <span class="hljs-type">String</span>

    <span class="hljs-class"><span class="hljs-keyword">enum</span> <span class="hljs-title">Errors</span>: <span class="hljs-title">Error</span> </span>{
        <span class="hljs-keyword">case</span> tooShortOrTooLong
        <span class="hljs-keyword">case</span> invalidCharacters
    }

    <span class="hljs-keyword">static</span> <span class="hljs-keyword">let</span> minimumTrimmedLength = <span class="hljs-number">2</span>
    <span class="hljs-keyword">static</span> <span class="hljs-keyword">let</span> maximumTrimmedLength = <span class="hljs-number">100</span>
    <span class="hljs-keyword">static</span> <span class="hljs-keyword">let</span> allowedCharacters: <span class="hljs-type">CharacterSet</span> = .alphanumerics
        .union(.whitespaces)

    <span class="hljs-meta">@discardableResult</span>
    <span class="hljs-keyword">static</span> <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">evaluated</span><span class="hljs-params">(<span class="hljs-number">_</span> name: String)</span></span> <span class="hljs-keyword">throws</span> -&gt; <span class="hljs-type">String</span> {
        <span class="hljs-keyword">let</span> trimmed = name.trimmingCharacters(<span class="hljs-keyword">in</span>: .whitespacesAndNewlines)
        <span class="hljs-keyword">guard</span> (<span class="hljs-number">1</span>...<span class="hljs-number">100</span>).<span class="hljs-built_in">contains</span>(trimmed.<span class="hljs-built_in">count</span>) <span class="hljs-keyword">else</span> {
            <span class="hljs-keyword">throw</span> <span class="hljs-type">Errors</span>.tooShortOrTooLong
        }
        <span class="hljs-keyword">let</span> disallowedCharacters = allowedCharacters.inverted
        <span class="hljs-keyword">guard</span> trimmed.rangeOfCharacter(from: disallowedCharacters) == <span class="hljs-literal">nil</span> <span class="hljs-keyword">else</span> {
            <span class="hljs-keyword">throw</span> <span class="hljs-type">Errors</span>.invalidCharacters
        }
        <span class="hljs-keyword">return</span> trimmed
    }

    <span class="hljs-keyword">init</span>(from decoder: <span class="hljs-type">Decoder</span>) <span class="hljs-keyword">throws</span> {
        <span class="hljs-keyword">let</span> string = <span class="hljs-keyword">try</span> decoder
            .singleValueContainer()
            .decode(<span class="hljs-type">String</span>.<span class="hljs-keyword">self</span>)
        <span class="hljs-keyword">self</span>.value = <span class="hljs-keyword">try</span> <span class="hljs-type">Name</span>.evaluated(string)
    }
}
</code></pre>
<p>We begin the same way by defining the criteria and possible evaluation errors. Followed by the static function to perform the evaluation and the initializer.</p>
<hr />
<h1 id="heading-email"><strong>Email</strong></h1>
<p>Emails are one of the hardest to validate by syntax alone, and this can clearly be seen by the sheer amount of RegEx that exists, all different from one another, to perform the same task.. all succeeding or failing to various degrees. Ref: <a target="_blank" href="https://stackoverflow.com/questions/156430/is-regular-expression-recognition-of-an-email-address-hard">https://stackoverflow.com/questions/156430/is-regular-expression-recognition-of-an-email-address-hard</a></p>
<p><em>If you are looking for a strong Email syntax validation library, consider using</em><a target="_blank" href="https://github.com/ekscrypto/SwiftEmailValidator"><em>SwiftEmailValidator</em></a><em>available for free on GitHub.</em></p>
<pre><code class="lang-swift"><span class="hljs-keyword">import</span> SwiftEmailValidator

<span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">Email</span>: <span class="hljs-title">Codable</span> </span>{
    <span class="hljs-keyword">let</span> value: <span class="hljs-type">String</span>

    <span class="hljs-class"><span class="hljs-keyword">enum</span> <span class="hljs-title">Errors</span>: <span class="hljs-title">Error</span> </span>{
        <span class="hljs-keyword">case</span> incorrectlyFormatted
    }

    <span class="hljs-meta">@discardableResult</span>
    <span class="hljs-keyword">static</span> <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">evaluated</span><span class="hljs-params">(<span class="hljs-number">_</span> string: String)</span></span> <span class="hljs-keyword">throws</span> -&gt; <span class="hljs-type">String</span> {
        <span class="hljs-keyword">let</span> trimmed = string.trimmingCharacters(<span class="hljs-keyword">in</span>: .whitespacesAndNewlines)
        <span class="hljs-keyword">guard</span> <span class="hljs-type">EmailSyntaxValidator</span>.correctlyFormatted(trimmed) <span class="hljs-keyword">else</span> {
            <span class="hljs-keyword">throw</span> incorrectlyFormatted
        }
        <span class="hljs-keyword">return</span> trimmed
    }

    <span class="hljs-keyword">init</span>(from decoder: <span class="hljs-type">Decoder</span>) <span class="hljs-keyword">throws</span> {
        <span class="hljs-keyword">let</span> string = <span class="hljs-keyword">try</span> decoder
            .singleValueContainer()
            .decode(<span class="hljs-type">String</span>.<span class="hljs-keyword">self</span>)
        <span class="hljs-keyword">self</span>.value = <span class="hljs-keyword">try</span> <span class="hljs-type">Email</span>.evaluated(string)
    }
}
</code></pre>
<hr />
<h1 id="heading-secure-client-struct"><strong>Secure “Client” struct</strong></h1>
<p>We can now go in our original Client struct and update the struct to use our new Codable values:</p>
<pre><code class="lang-swift"><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">Client</span>: <span class="hljs-title">Codable</span> </span>{
    <span class="hljs-keyword">let</span> name: <span class="hljs-type">Name</span>
    <span class="hljs-keyword">let</span> age: <span class="hljs-type">Age</span>
    <span class="hljs-keyword">let</span> email: <span class="hljs-type">Email</span>
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">decodeClientList</span><span class="hljs-params">(data: Data)</span></span> <span class="hljs-keyword">throws</span> -&gt; [<span class="hljs-type">Client</span>] {
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">try</span> <span class="hljs-type">JSONDecoder</span>().decode([<span class="hljs-type">Client</span>].<span class="hljs-keyword">self</span>, from: data)
}
</code></pre>
<hr />
<h1 id="heading-conclusion"><strong>Conclusion</strong></h1>
<p>It is fairly easy to greatly improve our handling of JSON data by defining custom data types with their own evaluation rules and initializers.</p>
<p>Remember that validation rules need to be evaluated against:</p>
<ul>
<li><p>Your business requirements</p>
</li>
<li><p>Intended demographics</p>
</li>
<li><p>RFC documents</p>
</li>
<li><p>Other libraries with which you interact (maximum variable length, CoreData SQL injections, …)</p>
</li>
<li><p>Other restrictions imposed by the database administrator, API, etc…</p>
</li>
</ul>
<p>Finally, always define unit tests to validate that your code works as intended.</p>
]]></content:encoded></item><item><title><![CDATA[Type enforced UserDefaults]]></title><description><![CDATA[On iOS the UserDefaults is a persistent dictionary stored and managed by iOS that survives the application runtime. It can be used to store and retrieve key-value pairs of different data types in-between user sessions of the app.
In this article, we ...]]></description><link>https://blog.encoded.life/type-enforced-userdefaults</link><guid isPermaLink="true">https://blog.encoded.life/type-enforced-userdefaults</guid><category><![CDATA[Swift]]></category><category><![CDATA[unit testing]]></category><category><![CDATA[data structures]]></category><category><![CDATA[clean code]]></category><dc:creator><![CDATA[Dave Poirier]]></dc:creator><pubDate>Tue, 14 Nov 2023 19:04:28 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1699988263288/cd92a334-d5c6-4228-9805-5089131e07cc.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>On iOS the <a target="_blank" href="https://developer.apple.com/documentation/foundation/userdefaults">UserDefaults</a> is a persistent dictionary stored and managed by iOS that survives the application runtime. It can be used to store and retrieve key-value pairs of different data types in-between user sessions of the app.</p>
<p>In this article, we will cover how to define a new class and type-specific enum to ensure that each key can only read/written by a specific data type.</p>
<h1 id="heading-problem-definition"><strong>Problem definition</strong></h1>
<p>Given a key for UserDefaults, you can store and retrieve any type. For example:</p>
<pre><code class="lang-swift"><span class="hljs-keyword">let</span> userDefaults = <span class="hljs-type">UserDefaults</span>.standard
userDefaults.<span class="hljs-keyword">set</span>(<span class="hljs-number">35</span>, forKey: <span class="hljs-string">"MyValue"</span>)
userDefaults.<span class="hljs-keyword">set</span>(<span class="hljs-string">"I love Swift"</span>, forKey: <span class="hljs-string">"MyValue"</span>)
</code></pre>
<p>In the example above, we initially set an integer type value for the key “MyValue” but then we overwrite that value with a string. If the value is read back expecting an Integer we might not get what we expected:</p>
<pre><code class="lang-swift"><span class="hljs-keyword">let</span> integerValue = userDefaults.integer(forKey: <span class="hljs-string">"MyValue"</span>)
<span class="hljs-built_in">print</span>(<span class="hljs-string">"MyValue:"</span>, integerValue)
<span class="hljs-comment">// MyValue: 0</span>
</code></pre>
<p>UserDefaults will silently store different types for the same key. It will also silently return to you automatically converted values between the last stored type for a given key and the type you are trying to retrieve.</p>
<p>While this flexibility may be useful, developers have to manually ensure they are using the correct data types associated to each key. Below is a method by which we can guarantee at compile time that any given key is only accessed using the expected type.</p>
<hr />
<h1 id="heading-enforcing-type-in-userdefaults"><strong>Enforcing Type in UserDefaults</strong></h1>
<p>Let’s start by creating a new class which will supervise read/write operations to UserDefaults for two types: Integer and String.</p>
<pre><code class="lang-swift"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">TypeSafeUserDefaults</span> </span>{

    <span class="hljs-comment">// Integer</span>
    <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">integer</span><span class="hljs-params">(forKey key: String)</span></span> -&gt; <span class="hljs-type">Int</span> {
        <span class="hljs-type">UserDefaults</span>.standard.integer(forKey: key)
    }

    <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">set</span><span class="hljs-params">(<span class="hljs-number">_</span> integer: Int, forKey key: String)</span></span> {
        <span class="hljs-type">UserDefaults</span>.standard.<span class="hljs-keyword">set</span>(integer, forKey: key)
    }

    <span class="hljs-comment">// String</span>
    <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">string</span><span class="hljs-params">(forKey key: String)</span></span> -&gt; <span class="hljs-type">String</span> {
        <span class="hljs-type">UserDefaults</span>.standard.string(forKey: key)
    }

    <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">set</span><span class="hljs-params">(<span class="hljs-number">_</span> string: String, forKey key: String)</span></span> {
        <span class="hljs-type">UserDefaults</span>.standard.<span class="hljs-keyword">set</span>(string, forKey: key)
    }
}
</code></pre>
<p>So far, nothing would prevent us from encountering the same issue we did with UserDefaults. We could very easily set either an Integer or a String for any given key. Let’s solve this:</p>
<pre><code class="lang-swift"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">TypeSafeUserDefaults</span> </span>{

    <span class="hljs-class"><span class="hljs-keyword">enum</span> <span class="hljs-title">IntegerKey</span>: <span class="hljs-title">StringLiteralType</span>, <span class="hljs-title">RawRepresentable</span> </span>{
        <span class="hljs-keyword">case</span> myIntegerValue
    }

    <span class="hljs-class"><span class="hljs-keyword">enum</span> <span class="hljs-title">StringKey</span>: <span class="hljs-title">StringLiteralType</span>, <span class="hljs-title">RawRepresentable</span> </span>{
        <span class="hljs-keyword">case</span> myStringValue
    }

    <span class="hljs-comment">// Integer</span>
    <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">integer</span><span class="hljs-params">(forKey key: IntegerKey)</span></span> -&gt; <span class="hljs-type">Int</span> {
        <span class="hljs-type">UserDefaults</span>.standard.integer(forKey: key.rawValue)
    }

    <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">set</span><span class="hljs-params">(<span class="hljs-number">_</span> integer: Int, forKey key: IntegerKey)</span></span> {
        <span class="hljs-type">UserDefaults</span>.standard.<span class="hljs-keyword">set</span>(integer, forKey: key.rawValue)
    }

    <span class="hljs-comment">// String</span>
    <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">string</span><span class="hljs-params">(forKey key: StringKey)</span></span> -&gt; <span class="hljs-type">String?</span> {
        <span class="hljs-type">UserDefaults</span>.standard.string(forKey: key.rawValue)
    }

    <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">set</span><span class="hljs-params">(<span class="hljs-number">_</span> string: String, forKey key: StringKey)</span></span> {
        <span class="hljs-type">UserDefaults</span>.standard.<span class="hljs-keyword">set</span>(string, forKey: key.rawValue)
    }
}
</code></pre>
<p>Let's see if it works....</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1699988438851/34523352-eb8b-41f4-86fe-1edc3cf171cc.webp" alt class="image--center mx-auto" /></p>
<p>Success! Our TypeSafeUserDefaults now prevents us from storing a string in a key expected to be used to store integers. Better yet, we get immediate feedback directly in Xcode and our project will fail to compile if we attempt to use the wrong type.</p>
<hr />
<h1 id="heading-ensuring-all-keys-are-unique"><strong>Ensuring all keys are unique</strong></h1>
<p>While the TypeSafeUserDefaults prevents storing values of the wrong type, if the two enums define the same keys, we can still end up with the same issue. Consider:</p>
<pre><code class="lang-swift"><span class="hljs-class"><span class="hljs-keyword">enum</span> <span class="hljs-title">IntegerKey</span>: <span class="hljs-title">StringLiteralType</span>, <span class="hljs-title">RawRepresentable</span> </span>{
  <span class="hljs-keyword">case</span> myValue
}

<span class="hljs-class"><span class="hljs-keyword">enum</span> <span class="hljs-title">StringKey</span>: <span class="hljs-title">StringLiteralType</span>, <span class="hljs-title">RawRepresentable</span> </span>{
  <span class="hljs-keyword">case</span> myValue
}
</code></pre>
<p>Unfortunately I haven’t found a way to get compile time errors from this. Luckily, Xcode supports unit tests so we can confirm our keys are unique by making our enum CaseIterable and using some unit tests. Let’s generate some enum with duplicated keys:</p>
<pre><code class="lang-swift"><span class="hljs-class"><span class="hljs-keyword">enum</span> <span class="hljs-title">IntegerKey</span>: <span class="hljs-title">StringLiteralType</span>, <span class="hljs-title">RawRepresentable</span>, <span class="hljs-title">CaseIterable</span> </span>{
    <span class="hljs-keyword">case</span> myIntegerValue
    <span class="hljs-keyword">case</span> myOtherValue
}

<span class="hljs-class"><span class="hljs-keyword">enum</span> <span class="hljs-title">StringKey</span>: <span class="hljs-title">StringLiteralType</span>, <span class="hljs-title">RawRepresentable</span>, <span class="hljs-title">CaseIterable</span> </span>{
    <span class="hljs-keyword">case</span> myStringValue
    <span class="hljs-keyword">case</span> myOtherValue
}
</code></pre>
<p>Now let’s implement a unit test to confirm all keys are unique:</p>
<pre><code class="lang-swift"><span class="hljs-keyword">import</span> XCTest
<span class="hljs-meta">@testable</span> <span class="hljs-keyword">import</span> TypeSafeUserDefaultsDemo

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">TypeSafeUserDefaultsEnumTests</span>: <span class="hljs-title">XCTestCase</span> </span>{

    <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">testKeysAreUnique</span><span class="hljs-params">()</span></span> {
        <span class="hljs-keyword">var</span> keysAlreadyEncountered: <span class="hljs-type">Set</span>&lt;<span class="hljs-type">String</span>&gt; = <span class="hljs-type">Set</span>()

        <span class="hljs-type">TypeSafeUserDefaults</span>.<span class="hljs-type">IntegerKey</span>.allCases.forEach {
            keysAlreadyEncountered.insert($<span class="hljs-number">0</span>.rawValue)
        }

        <span class="hljs-type">TypeSafeUserDefaults</span>.<span class="hljs-type">StringKey</span>.allCases.forEach {
            <span class="hljs-type">XCTAssertFalse</span>(keysAlreadyEncountered.<span class="hljs-built_in">contains</span>($<span class="hljs-number">0</span>.rawValue), <span class="hljs-string">"StringKey \($0) conflicts with an already defined key"</span>)
            keysAlreadyEncountered.insert($<span class="hljs-number">0</span>.rawValue)
        }
    }
}
</code></pre>
<p>When the unit tests run, we will now get this failure:</p>
<p><em>XCTAssertFalse failed — StringKey myOtherValue conflicts with an already defined key</em></p>
<hr />
<p>I hope this saves you hours of troubleshooting. Enjoy!</p>
]]></content:encoded></item><item><title><![CDATA[Managers! Don't use developer metrics.]]></title><description><![CDATA[I read an article on LinkedIn titled "What code quality metrics should you track to improve your programming?"
When management attempts to qualify the quality of their product and development processes, they often look for existing metrics to inform ...]]></description><link>https://blog.encoded.life/managers-dont-use-developer-metrics</link><guid isPermaLink="true">https://blog.encoded.life/managers-dont-use-developer-metrics</guid><category><![CDATA[performance metrics]]></category><category><![CDATA[opinion pieces]]></category><dc:creator><![CDATA[Dave Poirier]]></dc:creator><pubDate>Tue, 14 Nov 2023 18:51:32 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/JKUTrJ4vK00/upload/012bedaeb0bb0007c94409e0b5ad4a43.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I read an article on LinkedIn titled "<a target="_blank" href="https://www.linkedin.com/advice/0/what-code-quality-metrics-should-you-track-improve-mn3pc?contributionUrn=urn%3Ali%3Acomment%3A%28articleSegment%3A%28urn%3Ali%3AlinkedInArticle%3A7128144504271884288%2C7128144506134155264%29%2C7129942884581076992%29&amp;trk=rtyc"><strong>What code quality metrics should you track to improve your programming?</strong></a><strong>"</strong></p>
<p>When management attempts to qualify the quality of their product and development processes, they often look for existing metrics to inform their decisions.</p>
<p>Unfortunately...</p>
<p><strong>Most automated code quality metrics should not be utilized by management. The number of "issues" discovered, or the complexity of a function, are metrics intended for developers themselves, to help steer their work.</strong></p>
<p>In many instances, these metrics rely on false positives. Unless your team consistently reviews issues flagged by the automated tool, a significant portion of the identified issues will be false positives. Likewise, the tool will probably overlook more substantial concerns, such as excessively complex architecture.</p>
<p>The metrics that matters the most are:</p>
<ul>
<li><p><em>How long it takes for a new programmer on the team to understand what the code does?</em></p>
</li>
<li><p><em>How happy are the developers working on the code base?</em></p>
</li>
</ul>
<p>By focusing on reducing the amount of time it takes to onboard a new developer, you are indirectly making the code easier to understand. Code that is easier to understand inevitably leads to fewer defects.</p>
<p>Focusing on making your developers happy leads to greater productivity and less turnover.</p>
<p>Finally, if you really want to focus on producing quality, look at the customer/user reviews.</p>
]]></content:encoded></item><item><title><![CDATA[SwiftUI Opinion: ViewModel doesn't belong in Previews]]></title><description><![CDATA[SwiftUI Previews are key to making SwiftUI development fast and easy. However, if your views require a ViewModel, you are impeding your own development speed and re-usability of the view.
Let’s imagine that you create a view and it’s view model. Ever...]]></description><link>https://blog.encoded.life/swiftui-opinion-viewmodel-doesnt-belong-in-previews</link><guid isPermaLink="true">https://blog.encoded.life/swiftui-opinion-viewmodel-doesnt-belong-in-previews</guid><category><![CDATA[iOS]]></category><category><![CDATA[Swift]]></category><category><![CDATA[SwiftUI]]></category><category><![CDATA[MVVM]]></category><category><![CDATA[software architecture]]></category><dc:creator><![CDATA[Dave Poirier]]></dc:creator><pubDate>Tue, 14 Nov 2023 18:38:22 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1699986898051/d47d951c-00c8-48e4-a08c-6cf95e497237.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>SwiftUI Previews are key to making SwiftUI development fast and easy. However, if your views require a ViewModel, you are impeding your own development speed and re-usability of the view.</p>
<p>Let’s imagine that you create a view and it’s view model. <mark>Everything works, the project is released and everyone is happy.</mark> A couple months after, a new feature is added and you need another view that looks 100% like the one you implemented, except the logic in how the view is used is entirely different.</p>
<p><mark>You find yourself having to either duplicate the SwiftUI view, or refactor the one you had created so it’s no longer tightly coupled, jeopardizing the stability of the already developed feature.</mark></p>
<p>If your view requires interaction with a view model, you should extract all the visible parts of the view into its own SwiftUI view, keeping it agnostic of the view model. You end up with two SwiftUI views, one with knowledge of the view model that doesn’t have any design whatsoever in it, and another with all the design and no reference to the view model.</p>
<hr />
<h2 id="heading-example-of-what-not-to-do"><strong>Example of what NOT to do:</strong></h2>
<pre><code class="lang-swift"><span class="hljs-keyword">import</span> SwiftUI

<span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">BadDesignView</span>: <span class="hljs-title">View</span> </span>{
    @<span class="hljs-type">StateObject</span> <span class="hljs-keyword">var</span> viewModel: <span class="hljs-type">LoginViewModel</span>

    <span class="hljs-keyword">var</span> body: some <span class="hljs-type">View</span> {
        <span class="hljs-type">VStack</span> {
            <span class="hljs-keyword">if</span> viewModel.showLoginForm {
                <span class="hljs-type">TextField</span>(<span class="hljs-string">"Username"</span>, text: $viewModel.enteredUserName)
                <span class="hljs-type">Button</span>(<span class="hljs-string">"Login"</span>, action: viewModel.login)
            }
            <span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> username = viewModel.loggedInUser {
                <span class="hljs-type">Text</span>(<span class="hljs-string">"Greetings, \(username)"</span>)
            }
        }
    }
}

<span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">BadDesignView_Previews</span>: <span class="hljs-title">PreviewProvider</span> </span>{

    <span class="hljs-keyword">static</span> <span class="hljs-keyword">let</span> loggedInVm: <span class="hljs-type">LoginViewModel</span> = {
        <span class="hljs-keyword">let</span> vm = <span class="hljs-type">LoginViewModel</span>()
        vm.enteredUserName = <span class="hljs-string">"Mike Mousey"</span>
        vm.login()
        <span class="hljs-keyword">return</span> vm
    }()

    <span class="hljs-keyword">static</span> <span class="hljs-keyword">let</span> loggedOutVm: <span class="hljs-type">LoginViewModel</span> = <span class="hljs-type">LoginViewModel</span>()

    <span class="hljs-keyword">static</span> <span class="hljs-keyword">var</span> previews: some <span class="hljs-type">View</span> {
        <span class="hljs-type">BadDesignView</span>(viewModel: loggedOutVm)
            .previewDisplayName(<span class="hljs-string">"Logged Out"</span>)
            .previewLayout(.sizeThatFits)

        <span class="hljs-type">BadDesignView</span>(viewModel: loggedInVm)
            .previewDisplayName(<span class="hljs-string">"Logged In"</span>)
            .previewLayout(.sizeThatFits)
    }
}
</code></pre>
<hr />
<h2 id="heading-example-of-reusable-design"><strong>Example of reusable design:</strong></h2>
<pre><code class="lang-swift"><span class="hljs-keyword">import</span> SwiftUI

<span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">ReusableDesignView</span>: <span class="hljs-title">View</span> </span>{

    <span class="hljs-keyword">let</span> showLoginForm: <span class="hljs-type">Bool</span>
    @<span class="hljs-type">Binding</span> <span class="hljs-keyword">var</span> enteredUserName: <span class="hljs-type">String</span>
    <span class="hljs-keyword">let</span> loggedInUser: <span class="hljs-type">String?</span>
    <span class="hljs-keyword">let</span> loginAction: () -&gt; <span class="hljs-type">Void</span>

    <span class="hljs-keyword">var</span> body: some <span class="hljs-type">View</span> {
        <span class="hljs-type">VStack</span> {
            <span class="hljs-keyword">if</span> showLoginForm {
                <span class="hljs-type">TextField</span>(<span class="hljs-string">"Username"</span>, text: $enteredUserName)
                <span class="hljs-type">Button</span>(<span class="hljs-string">"Login"</span>, action: loginAction)
            }
            <span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> username = loggedInUser {
                <span class="hljs-type">Text</span>(<span class="hljs-string">"Greetings, \(username)"</span>)
            }
        }
    }
}

<span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">ReusableDesignView_Previews</span>: <span class="hljs-title">PreviewProvider</span> </span>{
    <span class="hljs-keyword">static</span> <span class="hljs-keyword">var</span> previews: some <span class="hljs-type">View</span> {
        <span class="hljs-type">ReusableDesignView</span>(
            showLoginForm: <span class="hljs-literal">true</span>,
            enteredUserName: .constant(<span class="hljs-string">""</span>),
            loggedInUser: <span class="hljs-literal">nil</span>,
            loginAction: { <span class="hljs-comment">/* do nothing */</span> })

        <span class="hljs-type">ReusableDesignView</span>(
            showLoginForm: <span class="hljs-literal">false</span>,
            enteredUserName: .constant(<span class="hljs-string">""</span>),
            loggedInUser: <span class="hljs-string">"Mike Mousey"</span>,
            loginAction: { <span class="hljs-comment">/* do nothing */</span> })
    }
}
</code></pre>
<pre><code class="lang-swift"><span class="hljs-keyword">import</span> SwiftUI

<span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">LoginView</span>: <span class="hljs-title">View</span> </span>{
    @<span class="hljs-type">StateObject</span> <span class="hljs-keyword">var</span> viewModel = <span class="hljs-type">LoginViewModel</span>()

    <span class="hljs-keyword">var</span> body: some <span class="hljs-type">View</span> {
        <span class="hljs-type">ReusableDesignView</span>(
            showLoginForm: viewModel.showLoginForm,
            enteredUserName: $viewModel.enteredUserName,
            loggedInUser: viewModel.loggedInUser,
            loginAction: viewModel.login)
    }
}
</code></pre>
<p>In a MVVM architecture, the logic is moved into a view model to allow testing the logic separately from the user interface.</p>
<blockquote>
<p><em>Isolating a designed view from the view model improves re-usability of the view.</em></p>
</blockquote>
<p>As a side effect, creating your Previews will be easier. You no longer have to find a way to bring the view model into the expected state, or have to create mock view models.</p>
]]></content:encoded></item><item><title><![CDATA[Learning about Mobile App Security]]></title><description><![CDATA[For mobile security, there are a few companies offering courses/certification but they are fairly expensive.  Security Compass, and Secure Code Warrior, would be my recommendations if you want to go that way. The community is split on whether that ex...]]></description><link>https://blog.encoded.life/learning-about-mobile-app-security</link><guid isPermaLink="true">https://blog.encoded.life/learning-about-mobile-app-security</guid><category><![CDATA[iOS]]></category><category><![CDATA[Swift]]></category><category><![CDATA[security testing ]]></category><category><![CDATA[blueteam]]></category><category><![CDATA[mobile app development]]></category><dc:creator><![CDATA[Dave Poirier]]></dc:creator><pubDate>Tue, 14 Nov 2023 16:15:48 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/FXFz-sW0uwo/upload/3885385d3227aaaea658c8c433c4c2ba.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>For mobile security, there are a few companies offering courses/certification but they are fairly expensive.  Security Compass, and Secure Code Warrior, would be my recommendations if you want to go that way. The community is split on whether that experience was positive for them.  </p>
<p>If you want to study on your own, make sure you understand Symmetric and Asymmetric cryptography, how to use CryptoKit, how to encrypt/decrypt files using AES GCM, how to sign and verify signature of data using P256, how to generate a symmetric key using asymmetric keys using P256 ECDH key derivation.  </p>
<p>You will also want to understand how to use Apple's TouchId and FaceID, how to use the SecureEnclave, the Keychain.  </p>
<p>Make sure you know how to enable iOS file protection when  writing files to disk (NSFileProtectionComplete) and how you can also set this by default using App Entitlements.  </p>
<p>There's also Runtime Application Self-Protection (RASP), TLS Pinning, making sure you account for TLS bypass attacks.  </p>
<p>Understanding mobile security also means knowing which tools the hackers use, so get familiar with MobSF, Frida, Ghidra, and Radare2. Be aware that like most topics, this is a rabbit hole from which you may never reach the end.</p>
<p>Other stuff you may want to brush up on:</p>
<ul>
<li><p>Data validation</p>
</li>
<li><p>User input sanitation</p>
</li>
<li><p>Data categorization (PII, public, private, classified, export controlled, etc)</p>
</li>
<li><p>Data retention</p>
</li>
<li><p>Privacy cover screen</p>
</li>
<li><p>Secure text fields</p>
</li>
<li><p>API documentation (defining maximum lengths, allowed characters, date formats, integer ranges, nullable fields, etc)</p>
</li>
<li><p>Memory safety</p>
</li>
<li><p>Objective-C method swizzling</p>
</li>
<li><p>AppDelegate swizzling</p>
</li>
<li><p>Red Team / Blue Team</p>
</li>
</ul>
<p>Make sure to also watch security forums, read up on all the latest zero-days. Hackers keep innovating and discovering new ways to compromise our systems so we have to find new ways to defend our apps!</p>
<p>Go blue team!</p>
]]></content:encoded></item><item><title><![CDATA[iOS: Protecting against TLS bypass attacks]]></title><description><![CDATA[The cat and mouse game against hacker never ends. While we can never fully prevent against hackers modifying our runtime binaries and bypass our security measures, we sure can make it more difficult for them.
TLS Pinning is a method by which we setup...]]></description><link>https://blog.encoded.life/ios-protecting-against-tls-bypass-attacks</link><guid isPermaLink="true">https://blog.encoded.life/ios-protecting-against-tls-bypass-attacks</guid><category><![CDATA[iOS]]></category><category><![CDATA[Swift]]></category><category><![CDATA[Mobile Development]]></category><category><![CDATA[securityawareness]]></category><dc:creator><![CDATA[Dave Poirier]]></dc:creator><pubDate>Tue, 14 Nov 2023 15:48:48 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1699976834921/e20f617b-ad64-4044-bca1-76f35e3c8737.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>The cat and mouse game against hacker never ends. While we can never fully prevent against hackers modifying our runtime binaries and bypass our security measures, we sure can make it more difficult for them.</p>
<p>TLS Pinning is a method by which we setup a URLSession delegate, during the TLS handshake the delegate will compare the public certificate against an expected certificate.</p>
<p>Hackers have since developed a method to bypass this using a tool named Objection, distributed as part of Frida. Using this tool, they use the .dylib loading mechanism to intercept and redirect SSL library calls. They can then immediately accept all certificate requests, all with a single command.</p>
<p>When they do this, the two attacks they can perform is to either skip the app URLSession delegate method entirely, or change the response from the URLSession delegate from Rejected to Accepted.</p>
<p>So how can we protect against this?</p>
<p>In the solution below, when a secure communication is required, we create a brand new URLSession and its delegate. A query is made and verify the TLS certificate like we usually do for TLS Pinning. However, we also record whether or not the TLS certificate was validated and if it passed validation.</p>
<p>Upon receiving the data/response, we double-check with the delegate whether it received a certificate validation request and whether the validation was successful.</p>
<p>Assuming either of the current Objection attacks, this method would correctly detect a TLS validation bypass attack. If they skip the validation and return data, it will be detected. And if they override the rejection with an acceptation, since we are performing the extra step of double-checking with our custom used-once delegate, it will also detect it.</p>
<h1 id="heading-secureurlsession"><strong>SecureURLSession</strong></h1>
<pre><code class="lang-swift"><span class="hljs-keyword">final</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">SecureURLSession</span> </span>{

    <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Delegate</span>: <span class="hljs-title">NSObject</span>, <span class="hljs-title">URLSessionDelegate</span> </span>{
        <span class="hljs-keyword">private</span> <span class="hljs-keyword">let</span> expectedHost: <span class="hljs-type">String</span>
        <span class="hljs-keyword">var</span> validatedChallenge: () -&gt; <span class="hljs-type">Void</span> = { <span class="hljs-comment">/* to be set later */</span> }
        <span class="hljs-keyword">private</span> <span class="hljs-keyword">let</span> allowedSignatures: [<span class="hljs-type">Data</span>]

        <span class="hljs-keyword">init</span>(host: <span class="hljs-type">String</span>, allowedSignatures signatures: [<span class="hljs-type">Data</span>]) {
            expectedHost = host
            allowedSignatures = signatures
            <span class="hljs-keyword">super</span>.<span class="hljs-keyword">init</span>()
        }

        <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">urlSession</span><span class="hljs-params">(
            <span class="hljs-number">_</span> session: URLSession,
            didReceive challenge: URLAuthenticationChallenge,
            completionHandler: @escaping @Sendable <span class="hljs-params">(URLSession.AuthChallengeDisposition, URLCredential?)</span></span></span> -&gt; <span class="hljs-type">Void</span>
        ) {
            <span class="hljs-keyword">guard</span> expectedHost == challenge.protectionSpace.host,
                  <span class="hljs-keyword">let</span> serverTrust = challenge.protectionSpace.serverTrust,
                  <span class="hljs-keyword">let</span> signature = signatureFromTrust(serverTrust),
                  allowedSignatures.<span class="hljs-built_in">contains</span>(signature)
            <span class="hljs-keyword">else</span> {
                completionHandler(.rejectProtectionSpace, <span class="hljs-literal">nil</span>)
                <span class="hljs-keyword">return</span>
            }

            validatedChallenge()
            completionHandler(.useCredential, challenge.proposedCredential)
        }

        <span class="hljs-keyword">private</span> <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">signatureFromTrust</span><span class="hljs-params">(<span class="hljs-number">_</span> trust: SecTrust)</span></span> -&gt; <span class="hljs-type">Data?</span> {
            <span class="hljs-keyword">guard</span> <span class="hljs-type">SecTrustGetCertificateCount</span>(trust) &gt; <span class="hljs-number">0</span>,
                  <span class="hljs-keyword">let</span> certificate = <span class="hljs-type">SecTrustGetCertificateAtIndex</span>(trust, <span class="hljs-number">0</span>)
            <span class="hljs-keyword">else</span> {
                <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>
            }

            <span class="hljs-keyword">let</span> certificateData = <span class="hljs-type">SecCertificateCopyData</span>(certificate) <span class="hljs-keyword">as</span> <span class="hljs-type">Data</span>
            <span class="hljs-keyword">let</span> digest = <span class="hljs-type">SHA256</span>.hash(data: certificateData)
            <span class="hljs-keyword">return</span> <span class="hljs-type">Data</span>(digest)
        }
    }

    <span class="hljs-keyword">init</span>(configuration: <span class="hljs-type">URLSessionConfiguration</span> = .<span class="hljs-keyword">default</span>, host: <span class="hljs-type">String</span>, allowedSignatures: [<span class="hljs-type">Data</span>]) {
        <span class="hljs-keyword">let</span> sessionDelegate = <span class="hljs-type">Delegate</span>(host: host, allowedSignatures: allowedSignatures)
        session = <span class="hljs-type">URLSession</span>(
            configuration: configuration,
            delegate: sessionDelegate,
            delegateQueue: <span class="hljs-literal">nil</span>)
        delegate = sessionDelegate
        delegate.validatedChallenge = { [<span class="hljs-keyword">weak</span> <span class="hljs-keyword">self</span>] <span class="hljs-keyword">in</span> <span class="hljs-keyword">self</span>?.continuation = { <span class="hljs-literal">true</span> } }
    }

    <span class="hljs-keyword">private</span> <span class="hljs-keyword">let</span> delegate: <span class="hljs-type">Delegate</span>
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">let</span> session: <span class="hljs-type">URLSession</span>
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">var</span> continuation: () -&gt; <span class="hljs-type">Bool</span> = { <span class="hljs-literal">false</span> }

    <span class="hljs-class"><span class="hljs-keyword">enum</span> <span class="hljs-title">Errors</span>: <span class="hljs-title">Error</span> </span>{
        <span class="hljs-keyword">case</span> sslBypassDetected
    }

    <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">data</span><span class="hljs-params">(from url: URL)</span></span> async <span class="hljs-keyword">throws</span> -&gt; (<span class="hljs-type">Data</span>, <span class="hljs-type">URLResponse</span>) {
        <span class="hljs-keyword">let</span> (data, response) = <span class="hljs-keyword">try</span> await session.data(from: url)
        <span class="hljs-keyword">guard</span> continuation() <span class="hljs-keyword">else</span> {
            <span class="hljs-keyword">throw</span> <span class="hljs-type">Errors</span>.sslBypassDetected
        }
        <span class="hljs-keyword">return</span> (data, response)
    }
}
</code></pre>
<h1 id="heading-how-to-use"><strong>How to use</strong></h1>
<p>And here’s a sample code securely fetching an Emoji using TLS Pinning while preventing TLS bypass:</p>
<pre><code class="lang-swift"><span class="hljs-class"><span class="hljs-keyword">extension</span> <span class="hljs-title">URLResponse</span> </span>{
    <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">isHttpWithStatusCode</span><span class="hljs-params">(<span class="hljs-number">_</span> statusCode: Int)</span></span> -&gt; <span class="hljs-type">Bool</span> {
        <span class="hljs-keyword">guard</span> <span class="hljs-keyword">let</span> httpResponse = <span class="hljs-keyword">self</span> <span class="hljs-keyword">as</span>? <span class="hljs-type">HTTPURLResponse</span> <span class="hljs-keyword">else</span> {
            <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>
        }
        <span class="hljs-keyword">return</span> httpResponse.statusCode == statusCode
    }
}

<span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">FetchEmojiActivity</span>: <span class="hljs-title">Sendable</span> </span>{

    <span class="hljs-class"><span class="hljs-keyword">enum</span> <span class="hljs-title">Errors</span>: <span class="hljs-title">Error</span> </span>{
        <span class="hljs-keyword">case</span> unexpectedServerResponse
    }

    <span class="hljs-comment">/// {</span>
    <span class="hljs-comment">///   "name": "hugging face",</span>
    <span class="hljs-comment">///   "category": "smileys and people",</span>
    <span class="hljs-comment">///   "group": "face positive",</span>
    <span class="hljs-comment">///   "htmlCode": ["&amp;#129303;"],</span>
    <span class="hljs-comment">///   "unicode": ["U+1F917"]</span>
    <span class="hljs-comment">/// }</span>
    <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">DTO</span>: <span class="hljs-title">Decodable</span> </span>{
        <span class="hljs-keyword">let</span> name: <span class="hljs-type">String</span>
        <span class="hljs-keyword">let</span> unicode: [<span class="hljs-type">String</span>]
    }

    <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">begin</span><span class="hljs-params">()</span></span> async <span class="hljs-keyword">throws</span> -&gt; <span class="hljs-type">String</span> {
        <span class="hljs-keyword">let</span> emojiHubTlsSignature = <span class="hljs-type">Data</span>(base64Encoded: <span class="hljs-string">"88bp12uxOpEr+AukM0O/I/9Jt+/gYpN+u2FPiLeD8tI="</span>)! &lt;-- <span class="hljs-keyword">do</span> not store your security hash <span class="hljs-keyword">in</span> a string <span class="hljs-keyword">in</span> a <span class="hljs-type">Production</span>
        <span class="hljs-keyword">let</span> secureUrlSession = <span class="hljs-type">SecureURLSession</span>(
            host: <span class="hljs-string">"emojihub.yurace.pro"</span>,
            allowedSignatures: [emojiHubTlsSignature]
        )
        <span class="hljs-keyword">let</span> randomEmojiUrl = <span class="hljs-type">URL</span>(string: <span class="hljs-string">"https://emojihub.yurace.pro/api/random"</span>)!
        <span class="hljs-keyword">let</span> (data, response) = <span class="hljs-keyword">try</span> await secureUrlSession.data(from: randomEmojiUrl)
        <span class="hljs-keyword">guard</span> response.isHttpWithStatusCode(<span class="hljs-number">200</span>) <span class="hljs-keyword">else</span> {
            <span class="hljs-keyword">throw</span> <span class="hljs-type">Errors</span>.unexpectedServerResponse
        }

        <span class="hljs-keyword">let</span> dto = <span class="hljs-keyword">try</span> <span class="hljs-type">JSONDecoder</span>().decode(<span class="hljs-type">DTO</span>.<span class="hljs-keyword">self</span>, from: data)
        <span class="hljs-keyword">let</span> emoji = dto.unicode
            .<span class="hljs-built_in">compactMap</span> { (<span class="hljs-number">_</span> seq: <span class="hljs-type">String</span>) -&gt; <span class="hljs-type">String?</span> <span class="hljs-keyword">in</span>
                <span class="hljs-keyword">guard</span> <span class="hljs-keyword">let</span> int = <span class="hljs-type">Int</span>(seq.replacingOccurrences(of: <span class="hljs-string">"U+"</span>, with: <span class="hljs-string">""</span>), radix: <span class="hljs-number">16</span>),
                      <span class="hljs-keyword">let</span> scalar = <span class="hljs-type">UnicodeScalar</span>(int)
                <span class="hljs-keyword">else</span> {
                    <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>
                }
                <span class="hljs-keyword">return</span> <span class="hljs-type">String</span>(scalar)
            }
            .joined()
        <span class="hljs-keyword">return</span> emoji
    }
}
</code></pre>
<p><strong>CAVEAT</strong>: For sake of simplicity, the TLS certificate in the FetchEmojiActivity is included as a string. <mark>This should be avoided unless you have some kind of cryptographic signature validation elsewhere in your code to ensure the hacker has not replaced it with its own validation hash.</mark></p>
<h1 id="heading-limitations-and-usage"><strong>Limitations and Usage</strong></h1>
<p>We shouldn’t be re-using the above SecureURLSession instance for multiple queries. Reusing the SecureURLSession class above for multiple queries would allow the attacker to bypass the TLS validation after the first request is made.</p>
<p>This will have an impact on performance. Doing this for background URLSession is not recommended as it will trigger your app to be throttled by the system.</p>
<p>The mechanism described here should only be used when the utmost security is required, like when bootstrapping a public/private asymmetric key exchange with the server.</p>
<p><strong>Remember to implement TLS certificate update mechanism in your application</strong>. Your TLS certificate will eventually expire or may have to be revoked; you will want your current app installations to continue working after you publish a new TLS certificate on your server. Consider using LaunchDarkly or other similar services to transmit an encrypted hash overriding the built-in hash.</p>
<p>If your application needs to do multiple parallel server queries or background transfers, consider using symmetric cryptography for your API queries &amp; responses on top of the standard URLSession without TLS pinning; limit the use of SecureURLSession to transfer your encryption keys and certificates.</p>
<h1 id="heading-conclusion"><strong>Conclusion</strong></h1>
<p>It seems possible to prevent <strong>typical</strong> TLS pinning bypass attacks by creating a custom URLSession and delegate when making a new request.</p>
<p>It’s impossible to fully protect against all attacks. If the attacker is able to do a memory dump of your process, use a debugger, modify your binary executable, there’s not much you can do. Clever attackers may even be able to acquire the master secret used to establish the TLS connection along with the exchanged keys then combined with Pcap gain access to all encrypted traffic. The best protection against these kinds of attack is Runtime Application Self-Protection (RASP).</p>
<p>However, as with anything, an extra layer of security means fewer hackers able to pull off the attack, or at the very least more efforts required which may be just enough to discourage an attacker.</p>
<p>Assuming RASP is able to detect runtime intrusion, the mechanism here should be able to properly protect your TLS communications.</p>
<p>Stay safe!</p>
]]></content:encoded></item><item><title><![CDATA[iOS Mobile App Security Tips]]></title><description><![CDATA[I’ve been working in the software industry for over twenty years, touching all levels of security requirements. Below are my minimum recommendations for all apps:

The mobile app should retrieve only the minimum amount of information required to oper...]]></description><link>https://blog.encoded.life/ios-mobile-app-security-tips</link><guid isPermaLink="true">https://blog.encoded.life/ios-mobile-app-security-tips</guid><category><![CDATA[iOS]]></category><category><![CDATA[Swift]]></category><category><![CDATA[Mobile Development]]></category><category><![CDATA[securityawareness]]></category><dc:creator><![CDATA[Dave Poirier]]></dc:creator><pubDate>Tue, 14 Nov 2023 15:43:23 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1699976566878/c9378da8-33b2-40ba-b949-7dee6de80965.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I’ve been working in the software industry for over twenty years, touching all levels of security requirements. Below are my minimum recommendations for all apps:</p>
<ol>
<li><p>The mobile app should retrieve only the minimum amount of information required to operate from the servers.</p>
</li>
<li><p>All user data stored by the app, whether on disk, Keychain, or <code>UserDefaults</code>, should be encrypted. Do not rely on the Keychain to do the encryption for you or the iOS to enforce its protection mode.</p>
</li>
<li><p>Use TLS pinning to validate that the communication to/from the server is secure and not intercepted by a “trusted” certificate on the device. Make sure to also build an update mechanism so the mobile app installations can continue working when your server certificate is updated.</p>
</li>
<li><p>When recording logs or analytics, make sure not to record any PII (Personally Identifiable Information). You can use encryption, cryptographic hashing or tokenization, or better yet, keep the information recorded as generic as possible. Make sure those same logs and analytics do not record any API response data unless that data is once again encrypted.</p>
</li>
<li><p>If you store API keys or software licenses in your mobile application, do not store the information in the <code>Info.plist</code> or in strings. Even strings injected by your CI/CD pipeline at build time are unsafe.<br /> If possible, use server services, and only if required, use encryption to protect your API keys and licenses and only decrypt them in memory at runtime. Never store the decrypted keys anywhere.</p>
</li>
<li><p>Use only trusted third-party libraries and frameworks. If you cannot vouch for the identity or trustworthiness of the library's maintainer, you shouldn’t use it. If you insist on using it, you should do an in-depth security assessment at every update you want to include, then use cryptographic signature validation to ensure the authenticity of the library hasn’t been compromised.</p>
</li>
<li><p>Use a privacy screen to cover the information displayed by the app when the app is pushed to the background. This will prevent iOS from automatically capturing screenshots of your app when it goes to the background, which could lead to PII leaks.</p>
</li>
<li><p>When your app receives information via copy/paste, or if the user can manually enter information in a field, always validate the information entered by the user to the maximum extent possible.<br /> Check for maximum length, allowed characters, and the values match expectations. If the information should never leave the app, make sure to set up and use a custom Pasteboard unique to your app.</p>
</li>
<li><p>Use biometrics carefully. It doesn’t validate the user's identity, only that this user can use biometrics to unlock it. Often, a family will set up multiple faces or fingerprints to unlock a device, leading to multiple users passing biometric validation.</p>
</li>
<li><p>Validate all the data you receive from API response or even data loaded from disk previously saved by your app. A third party may have compromised or modified this data if the device is hacked.</p>
</li>
<li><p>Never assume that a compromised device is intentional on the user's part. Hackers may remotely take control of the device using zero-day vulnerabilities, and the user may not be aware their device was compromised.</p>
</li>
</ol>
<p>While this list doesn’t fully represent the entire extent of security mechanisms, I find that it covers what I consider the very minimum that every app developer should do.</p>
<p>Even if you think the jogging route isn’t critical data, when a celebrity or diplomat enters the jogging route recorded in your app, leaking this data may risk personal harm to the individual.</p>
<p>If you have any doubts, play it safe. And remember to do a full threat assessment.</p>
<h1 id="heading-community-contributions"><strong>Community Contributions</strong></h1>
<p>12. Use the Swift language for new projects and allocate meaningful resources to your plan to migrate your codebase from Objective C to Swift. Use memory-safe languages for cloud server components.</p>
<p>13. Use automated unit tests to ensure your application architecture's designed security elements remain secure as your application evolves.</p>
<p>14. Use DocC to document the security features of your application design in the code so that future developers can understand the context and decisions made. This will help prevent security from being accidentally undermined by future changes to the system design.</p>
<p>Here’s a reference for <a target="_blank" href="https://developer.apple.com/documentation/docc">more information</a>.</p>
<p>For a more complete guide to application security, you should refer to the OWASP MAS by <a target="_blank" href="https://mas.owasp.org/">clicking this link</a>.</p>
]]></content:encoded></item></channel></rss>