<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://geekingwithmauri.com/feed.xml" rel="self" type="application/atom+xml" /><link href="https://geekingwithmauri.com/" rel="alternate" type="text/html" /><updated>2026-02-09T13:46:53+00:00</updated><id>https://geekingwithmauri.com/feed.xml</id><title type="html">Geeking with Mauri</title><subtitle>The main goal is for all of us to educate each other ☺️🤓 You&apos;re more than welcome to point out alternative approaches you might think for anything you read here.</subtitle><author><name>Mauricio Chirino</name></author><entry><title type="html">The easiest code I didn’t write</title><link href="https://geekingwithmauri.com/work/ai-plus-criticalThinkin.html" rel="alternate" type="text/html" title="The easiest code I didn’t write" /><published>2026-02-08T00:00:00+00:00</published><updated>2026-02-08T00:00:00+00:00</updated><id>https://geekingwithmauri.com/work/ai-plus-criticalThinkin</id><content type="html" xml:base="https://geekingwithmauri.com/work/ai-plus-criticalThinkin.html"><![CDATA[<!-- ------------ -->

<p style="text-align: center;"><img src="/assets/posts/19_criticalThinking-AI/cover.png" alt="dictionary" />
Automating rules (image generated with AI)</p>

<blockquote>
  <p>Implementation is getting cheaper. Thinking is not.</p>
</blockquote>

<ul>
  <li><a href="#a-tiny-mistake-with-a-big-blast-radius">A tiny mistake with a big blast radius</a></li>
  <li><a href="#enter-my-one-hit-song-unit-tests-">Enter my one-hit song: unit tests 🕺🏽</a></li>
  <li><a href="#letting-ai-sweat-the-small-stuff">Letting AI sweat the small stuff</a></li>
  <li><a href="#but-mauri-isnt-this-just-busywork">But Mauri, isn’t this just busywork?</a></li>
  <li><a href="#wiring-it-into-ci">Wiring it into CI</a></li>
  <li><a href="#ai-changes-the-how-not-the-why">AI changes the how, not the why</a></li>
  <li><a href="#the-real-skill-shift">The real skill shift</a></li>
  <li><a href="#closing-thoughts">Closing thoughts</a></li>
</ul>

<h2 id="a-tiny-mistake-with-a-big-blast-radius">A tiny mistake with a big blast radius</h2>

<p>Awhile ago I did something utterly boring at work: I added a new universal link to one of our well‑known configuration files. We do this every once in a while. No fireworks. No architectural diagrams. Just a new entry in a place that has seen dozens of them before.</p>

<p>Except this time I messed it up.</p>

<p><img src="https://geekingwithmauri.com/assets/posts/19_criticalThinking-AI/precursor.jpg" alt="trigger" /></p>

<p>The formatting was slightly off. Nothing dramatic at first glance. The kind of thing your brain happily autocorrects when you’ve been staring at YAML for too long. The scary part? By the time a teammate caught it, the change already had a couple of approvals (the minimum allowed by our internal policy to merge changes into the main branch).</p>

<p>Had this landed in production, it would have broken the entire <a href="https://developer.apple.com/documentation/xcode/supporting-associated-domains">.well-known endpoint crawled by Apple</a> for universal links. That familiar feeling kicked in: where you replay alternate timelines in your head where it all went to 💩 and then you had some VERY awkard explaining to do.</p>

<p>I’ve written before about how <a href="/work/thinkingBeforeCoding.html">fragile assumptions tend to hide in plain sight</a>. This was one of those moments, just wearing a YAML costume.</p>

<h2 id="enter-my-one-hit-song-unit-tests-">Enter my one-hit song: unit tests 🕺🏽</h2>

<p>This isn’t a story about blame or process failure. Code review worked. A human caught it. All good. But it did surface an uncomfortable question: What if the same mistake sneaks through next time?</p>

<p>The answer wasn’t “be more careful” (that never scales), but to remove this entire class of errors from the equation. So I did what any reasonable engineer would do: I added tests.</p>

<p>Not app tests. Not snapshot tests. Just a small, boring, very targeted set of unit tests validating the configuration itself. I’ve argued before that <a href="/swift/testing3rdFwk.html">tests are about behavior, not implementation details</a>. This was the same idea, applied outside of application code.</p>

<h2 id="letting-ai-sweat-the-small-stuff">Letting AI sweat the small stuff</h2>

<p>With Copilot’s help and a bit of creative thinking, I put together a small Python script that validates the formatting of the configmap entries where these well‑known paths live. The goal was intentionally boring:</p>

<ol>
  <li>Parse the YAML</li>
  <li>Extract the values</li>
  <li>Assert that Apple expects a JSON object</li>
  <li>Assert that Android expects a JSON array</li>
</ol>

<p>Nothing clever. No abstractions to blog about. Just encoded assumptions turned into executable rules.</p>

<figure class="highlight"><pre><code class="language-py" data-lang="py"><span class="kn">import</span> <span class="nn">unittest</span>
<span class="kn">import</span> <span class="nn">yaml</span>
<span class="kn">import</span> <span class="nn">json</span>

<span class="k">class</span> <span class="nc">TestConfig</span><span class="p">(</span><span class="n">unittest</span><span class="p">.</span><span class="n">TestCase</span><span class="p">):</span>
    <span class="k">def</span> <span class="nf">setUp</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">files</span> <span class="o">=</span> <span class="p">[</span> <span class="c1"># I've obfuscated the real routes for security reasons.
</span>            <span class="p">(</span><span class="s">'dev'</span><span class="p">,</span> <span class="s">'deployments/.../dev.yaml'</span><span class="p">),</span>
            <span class="p">(</span><span class="s">'stage'</span><span class="p">,</span> <span class="s">'deployments/.../stage.yaml'</span><span class="p">),</span>
            <span class="p">(</span><span class="s">'prod'</span><span class="p">,</span> <span class="s">'deployments/.../prod.yaml'</span><span class="p">),</span>
            <span class="p">(</span><span class="s">'sandbox'</span><span class="p">,</span> <span class="s">'deployments/.../sandbox.yaml'</span><span class="p">),</span>
        <span class="p">]</span>

    <span class="k">def</span> <span class="nf">test_well_known_android_json</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="k">for</span> <span class="n">name</span><span class="p">,</span> <span class="n">file_path</span> <span class="ow">in</span> <span class="bp">self</span><span class="p">.</span><span class="n">files</span><span class="p">:</span>
            <span class="k">with</span> <span class="bp">self</span><span class="p">.</span><span class="n">subTest</span><span class="p">(</span><span class="nb">file</span><span class="o">=</span><span class="n">name</span><span class="p">):</span>
                <span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="n">file_path</span><span class="p">,</span> <span class="s">'r'</span><span class="p">)</span> <span class="k">as</span> <span class="nb">file</span><span class="p">:</span>
                    <span class="n">config</span> <span class="o">=</span> <span class="n">yaml</span><span class="p">.</span><span class="n">safe_load</span><span class="p">(</span><span class="nb">file</span><span class="p">)</span>
                <span class="n">data</span> <span class="o">=</span> <span class="n">config</span><span class="p">[</span><span class="s">'configmap'</span><span class="p">][</span><span class="s">'data'</span><span class="p">][</span><span class="s">'well_known_android'</span><span class="p">]</span>
                <span class="bp">self</span><span class="p">.</span><span class="n">assertIsInstance</span><span class="p">(</span><span class="n">json</span><span class="p">.</span><span class="n">loads</span><span class="p">(</span><span class="n">data</span><span class="p">.</span><span class="n">strip</span><span class="p">()),</span> <span class="nb">list</span><span class="p">)</span>

    <span class="k">def</span> <span class="nf">test_well_known_apple_json</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="k">for</span> <span class="n">name</span><span class="p">,</span> <span class="n">file_path</span> <span class="ow">in</span> <span class="bp">self</span><span class="p">.</span><span class="n">files</span><span class="p">:</span>
            <span class="k">with</span> <span class="bp">self</span><span class="p">.</span><span class="n">subTest</span><span class="p">(</span><span class="nb">file</span><span class="o">=</span><span class="n">name</span><span class="p">):</span>
                <span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="n">file_path</span><span class="p">,</span> <span class="s">'r'</span><span class="p">)</span> <span class="k">as</span> <span class="nb">file</span><span class="p">:</span>
                    <span class="n">config</span> <span class="o">=</span> <span class="n">yaml</span><span class="p">.</span><span class="n">safe_load</span><span class="p">(</span><span class="nb">file</span><span class="p">)</span>
                <span class="n">data</span> <span class="o">=</span> <span class="n">config</span><span class="p">[</span><span class="s">'configmap'</span><span class="p">][</span><span class="s">'data'</span><span class="p">][</span><span class="s">'well_known_apple'</span><span class="p">]</span>
                <span class="bp">self</span><span class="p">.</span><span class="n">assertIsInstance</span><span class="p">(</span><span class="n">json</span><span class="p">.</span><span class="n">loads</span><span class="p">(</span><span class="n">data</span><span class="p">.</span><span class="n">strip</span><span class="p">()),</span> <span class="nb">dict</span><span class="p">)</span></code></pre></figure>

<p>Did AI help write most of this? Absolutely.</p>

<p>Did AI decide what should be tested? Not even close.</p>

<h2 id="but-mauri-isnt-this-just-busywork">But Mauri, isn’t this just busywork?</h2>

<p>The thing is, this isn’t about the script. It’s about shifting responsibility from human memory to executable rules. Humans forget, YAML doesn’t care and JSON parsers are ruthless.</p>

<p>Once this test exists, a whole category of mistakes simply cannot reach production anymore. Not because people are smarter, but because the system is less forgiving. That’s leverage.</p>

<h2 id="wiring-it-into-ci">Wiring it into CI</h2>

<p>A test that only runs on your machine is a suggestion. So I wired it into CI using a small <a href="https://github.com/features/actions">GitHub Actions</a> workflow:</p>

<figure class="highlight"><pre><code class="language-yml" data-lang="yml"><span class="na">name</span><span class="pi">:</span> <span class="s">Config validator</span>
<span class="na">on</span><span class="pi">:</span>
  <span class="na">pull_request</span><span class="pi">:</span>
    <span class="na">branches</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="s">main</span>
    <span class="na">paths</span><span class="pi">:</span> <span class="c1"># again, real path obfuscated </span>
      <span class="pi">-</span> <span class="s1">'</span><span class="s">deployments/../**'</span> <span class="c1"># &lt;- One of my teammates suggested scoping this workflow only to changes in those files instead of running it everywhere. </span>

<span class="na">jobs</span><span class="pi">:</span>
  <span class="na">config_validator</span><span class="pi">:</span>
    <span class="na">runs-on</span><span class="pi">:</span> <span class="s">ubuntu-latest</span>
    <span class="na">steps</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Fetch repo</span>
        <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/checkout@v4</span>

      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Set up Python </span><span class="m">3.9</span>
        <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/setup-python@v5</span>
        <span class="na">with</span><span class="pi">:</span>
          <span class="na">python-version</span><span class="pi">:</span> <span class="s1">'</span><span class="s">3.9'</span>

      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Update package list</span>
        <span class="na">run</span><span class="pi">:</span> <span class="pi">|</span>
          <span class="s">sudo apt update</span>

      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Set up dependencies</span>
        <span class="na">run</span><span class="pi">:</span> <span class="pi">|</span>
          <span class="s">pip install --upgrade pip</span>
          <span class="s">pip install pyyaml</span>

      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Validate YAML configurations</span>
        <span class="na">run</span><span class="pi">:</span> <span class="s">python scripts/test_configmap.py</span></code></pre></figure>

<p>Even when AI helps write the code, humans still design the system.</p>

<h2 id="ai-changes-the-how-not-the-why">AI changes the how, not the why</h2>

<p>AI made the implementation details almost irrelevant. Writing Python, remembering APIs, parsing YAML — those are mechanical steps now.</p>

<p>What wasn’t automated:</p>

<ul>
  <li>Identifying the risk</li>
  <li>Understanding the blast radius</li>
  <li>Deciding which invariants matter</li>
  <li>Choosing <a href="https://docs.github.com/en/actions/reference/workflows-and-actions/workflow-syntax#onpushpull_requestpull_request_targetpathspaths-ignore">where in the pipeline to enforce them</a></li>
</ul>

<p>That’s the work. It’s the same pattern I touched on in <a href="/coding/fromSwiftToReact.html">my previous post</a>: tools change, mental models endure.</p>

<h2 id="the-real-skill-shift">The real skill shift</h2>

<p>If you squint, this story isn’t about Python, GitHub Actions, or configmaps. It’s about a shift in what it means to be effective as an engineer. Implementation used to be the bottleneck. Now it’s intent.</p>

<p>AI is excellent at answering “how do I write this?” but it’s still terrible at answering “should this exist?” and that’s fine. Because the creative act in software has never been about syntax. It’s about deciding where to draw lines, what to protect, and which assumptions deserve to be locked in with tests.</p>

<h2 id="closing-thoughts">Closing thoughts</h2>

<p>The real win wasn’t the script. It was turning a fragile assumption into a rule the system enforces for me. That’s the kind of help worth automating.</p>]]></content><author><name>Mauricio Chirino</name></author><category term="work" /><category term="coding" /><category term="ai" /><category term="engineering" /><category term="work" /><category term="ci" /><summary type="html"><![CDATA[If implementation is cheap now, where does real engineering actually happen?]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://geekingwithmauri.com/assets/resources/logo.png" /><media:content medium="image" url="https://geekingwithmauri.com/assets/resources/logo.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">From Swift to React Native</title><link href="https://geekingwithmauri.com/coding/fromSwiftToReact.html" rel="alternate" type="text/html" title="From Swift to React Native" /><published>2026-01-26T00:00:00+00:00</published><updated>2026-01-26T00:00:00+00:00</updated><id>https://geekingwithmauri.com/coding/fromSwiftToReact</id><content type="html" xml:base="https://geekingwithmauri.com/coding/fromSwiftToReact.html"><![CDATA[<!-- ------------ -->

<p style="text-align: center;"><img src="/assets/posts/18_swift2react/cover.png" alt="dictionary" />
The transition (image generated with AI)</p>

<blockquote>
  <p>There’s pride in mastery. But pride can also be a shackle.</p>
</blockquote>

<ul>
  <li><a href="#getting-over-the-dogma">Getting over the dogma</a></li>
  <li><a href="#javascript-the-frenemy">JavaScript: the Frenemy</a></li>
  <li><a href="#the-ecosystem-is-not-a-swamp">The ecosystem is not a swamp</a></li>
  <li><a href="#debugging-is-weird-at-first">Debugging is weird (at first)</a></li>
  <li><a href="#thinking-in-components">Thinking in Components</a></li>
  <li><a href="#navigation-isnt-trivial">Navigation isn’t trivial</a></li>
  <li><a href="#platform-parity-woes">Platform parity woes</a></li>
  <li><a href="#tooling-is-a-mixed-bag">Tooling is a mixed bag</a></li>
  <li><a href="#the-people-factor">The People factor</a></li>
  <li><a href="#well-oiled-ai">Well oiled AI</a></li>
  <li><a href="#but-mauri-what-about-performance">But Mauri, what about performance?</a></li>
  <li><a href="#side-by-side-swift-vs-typescript">Side-by-side: Swift vs TypeScript</a></li>
  <li><a href="#should-you-make-the-leap">Should you make the leap?</a></li>
</ul>

<p>After a decade in iOS development, React Native feels like opening a door to a room you’ve always known existed but never stepped into. You’ve walked past it a thousand times, maybe peeked through the keyhole now and then.</p>

<p>But when there’s such a radical shift in the industry and you’re forced in, you finally realize: it’s both familiar and bizarre.</p>

<h2 id="getting-over-the-dogma">Getting over the dogma</h2>

<p>The first challenge isn’t technical. It’s emotional. I’ve spent the last ten years crafting UIKit views, poking at Auto Layout constraints until they behaved, wrestling storyboards, and more recently, embracing <a href="/swift/localizationWithSwiftUI.html">SwiftUI’s declarative style</a>. There’s pride in that mastery. But pride can also be a shackle.</p>

<p>React Native forces you to let go of some of that. Not all of it —many principles carry over— but enough that you start questioning your instincts. Your intuition whispers, “Subclass that!” or “Use a delegate!” And React Native chuckles and replies, “How about a hook instead?”</p>

<h2 id="javascript-the-frenemy">JavaScript: the Frenemy</h2>

<p>Let’s talk about JavaScript. The language itself is quirky, and it’s easy to dismiss when you come from <a href="/swift/self-documented-code-with-flexible-swift-enums.html">Swift’s type-safe paradise</a>. But if you squint past the oddities, modern JavaScript (or really, TypeScript, which you’ll quickly adopt) is not the villain we make it out to be. It’s like that colleague who’s a bit chaotic but gets stuff done.</p>

<p>I still miss Swift’s exhaustiveness <a href="/swift/Self-descriptive-localizable.html">checks and protocol extensions</a>. But TypeScript gives me just enough guardrails to not fall off the bike. And the sheer flexibility of the language lets you move fast, prototype quicker, and rework things without waiting for a CI build to finish chewing on Xcode’s latest tantrum.</p>

<h2 id="the-ecosystem-is-not-a-swamp">The ecosystem is not a swamp</h2>

<p>There’s a popular meme in native circles that the JavaScript ecosystem is a fragile tower of dependencies waiting to collapse. And while there <em>is</em> truth to that (I see you, npm audit), it’s also unfair.</p>

<p>React Native has matured. Metro is reasonably fast, and the community around core libraries (React Navigation, Reanimated, etc.) is strong. You’ll still hit some friction points, but the pace of improvement is high. Bugs get fixed. APIs evolve. People care.</p>

<h2 id="debugging-is-weird-at-first">Debugging is weird (at first)</h2>

<p><a href="https://developer.apple.com/tutorials/instruments">Instruments</a> has no real equivalent. Debugging styles and layout bugs is not as clean as flipping on Xcode’s view debugger. But there’s Flipper. There’s React DevTools. You learn to console.log more than you’d like to admit. And honestly? Sometimes it’s faster.</p>

<p>Hot reloading changes everything. Waiting for a SwiftUI preview to kick in or running on-device after every minor tweak starts to feel ancient by comparison. This tight loop makes experimentation delightful.</p>

<h2 id="thinking-in-components">Thinking in Components</h2>

<p>This was the biggest mental shift.</p>

<p>React Native pushes you to think in components from day one. You stop seeing screens and start seeing pieces: buttons, headers, cards, sections. You pass props, manage local state, and build composable, declarative UIs.</p>

<p>And it’s liberating.</p>

<p>You begin asking, “What does this component <em>need</em> to know?” rather than stuffing things into a giant view controller or a bloated SwiftUI <code class="language-plaintext highlighter-rouge">View</code>. It’s like going from oil painting to Lego blocks. Less expressive in some ways, more structured in others.</p>

<h2 id="navigation-isnt-trivial">Navigation isn’t trivial</h2>

<p>Remember how intuitive <code class="language-plaintext highlighter-rouge">UINavigationController</code> felt when you first picked it up? React Navigation isn’t that. It works, but it requires setup, boilerplate, and reading more docs than you’d prefer.</p>

<p>But once you get past the initial friction, you realize it’s incredibly flexible. Stacks, tabs, modals —all configurable and nestable– in ways UIKit would protest. SwiftUI’s NavigationStack also tried to simplify things, but with each iOS release comes new breaking changes that make you wonder if anything will stabilize.</p>

<h2 id="platform-parity-woes">Platform parity woes</h2>

<p>You will hit those moments: a gesture behaves differently on Android. A font renders awkwardly. A third-party lib works great on iOS but breaks on Pixel 4.</p>

<p>This is the price you pay for code sharing. It’s a tax. Sometimes it’s minor. Sometimes it’s a migraine. You get better at identifying what should be shared and what needs to fork. And your empathy for Android devs grows exponentially.</p>

<h2 id="tooling-is-a-mixed-bag">Tooling is a mixed bag</h2>

<p>Xcode is still in the loop. Android Studio joins the party. And you’ll spend time in terminal windows running metro, resetting caches, and debugging weird node issues. It’s part of the territory.</p>

<p>But getting hot reload in both platforms at once next to Cursor feels like a superpower. The trade off worths it</p>

<h2 id="the-people-factor">The People factor</h2>

<p>React Native has a vibrant community. Blog posts, YouTube tutorials, Discord servers, open-source libraries—it’s all out there. People are building real apps, solving real problems, and sharing generously.</p>

<p>You feel it.</p>

<p>Coming from iOS, where most devs operate in silos or behind NDAs, this openness is refreshing. It reminds you that tech is a shared craft, not just a job.</p>

<h2 id="well-oiled-ai">Well oiled AI</h2>

<p>Following up in the people factor, there’s also AI in the loop. Such a mature and open ecosystem means LLMs got better training, thus better results when getting a hand from Claude/ChatGPT/your-AI-of-preference.</p>

<h2 id="but-mauri-what-about-performance">But Mauri, what about performance?</h2>

<p>Ah, the classic concern.</p>

<p>React Native isn’t native-native. But 99% of the time, that doesn’t matter. Unless you’re building a real-time 3D renderer or pushing thousands of concurrent animations, React Native’s performance is perfectly adequate.</p>

<p>And when you do need to go lower level? You can. Use the native modules bridge. Write a Swift or Kotlin module. Optimize the hot path. Just like you’d tune a performance-sensitive SwiftUI view with <code class="language-plaintext highlighter-rouge">drawingGroup</code> or <code class="language-plaintext highlighter-rouge">@ViewBuilder</code>.</p>

<h2 id="side-by-side-swift-vs-typescript">Side-by-side: Swift vs TypeScript</h2>

<p>Let’s say we’re showing a simple user card:</p>

<p><strong>SwiftUI:</strong></p>

<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">struct</span> <span class="kt">UserCard</span><span class="p">:</span> <span class="kt">View</span> <span class="p">{</span>
    <span class="k">let</span> <span class="nv">name</span><span class="p">:</span> <span class="kt">String</span>
    <span class="k">let</span> <span class="nv">age</span><span class="p">:</span> <span class="kt">Int</span>

    <span class="k">var</span> <span class="nv">body</span><span class="p">:</span> <span class="kd">some</span> <span class="kt">View</span> <span class="p">{</span>
        <span class="kt">VStack</span><span class="p">(</span><span class="nv">alignment</span><span class="p">:</span> <span class="o">.</span><span class="n">leading</span><span class="p">)</span> <span class="p">{</span>
            <span class="kt">Text</span><span class="p">(</span><span class="n">name</span><span class="p">)</span><span class="o">.</span><span class="nf">font</span><span class="p">(</span><span class="o">.</span><span class="n">headline</span><span class="p">)</span>
            <span class="kt">Text</span><span class="p">(</span><span class="s">"Age: </span><span class="se">\(</span><span class="n">age</span><span class="se">)</span><span class="s">"</span><span class="p">)</span><span class="o">.</span><span class="nf">font</span><span class="p">(</span><span class="o">.</span><span class="n">subheadline</span><span class="p">)</span>
        <span class="p">}</span>
        <span class="o">.</span><span class="nf">padding</span><span class="p">()</span>
        <span class="o">.</span><span class="nf">background</span><span class="p">(</span><span class="kt">Color</span><span class="o">.</span><span class="n">gray</span><span class="o">.</span><span class="nf">opacity</span><span class="p">(</span><span class="mf">0.2</span><span class="p">))</span>
        <span class="o">.</span><span class="nf">cornerRadius</span><span class="p">(</span><span class="mi">8</span><span class="p">)</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p><strong>React Native (TypeScript):</strong></p>

<div class="language-tsx highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">import</span> <span class="nx">React</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">react</span><span class="dl">'</span><span class="p">;</span>
<span class="k">import</span> <span class="p">{</span> <span class="nx">View</span><span class="p">,</span> <span class="nx">Text</span><span class="p">,</span> <span class="nx">StyleSheet</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">react-native</span><span class="dl">'</span><span class="p">;</span>

<span class="kd">type</span> <span class="nx">Props</span> <span class="o">=</span> <span class="p">{</span>
  <span class="na">name</span><span class="p">:</span> <span class="kr">string</span><span class="p">;</span>
  <span class="nl">age</span><span class="p">:</span> <span class="kr">number</span><span class="p">;</span>
<span class="p">};</span>

<span class="kd">const</span> <span class="nx">UserCard</span><span class="p">:</span> <span class="nx">React</span><span class="p">.</span><span class="nx">FC</span><span class="o">&lt;</span><span class="nx">Props</span><span class="o">&gt;</span> <span class="o">=</span> <span class="p">({</span> <span class="nx">name</span><span class="p">,</span> <span class="nx">age</span> <span class="p">})</span> <span class="o">=&gt;</span> <span class="p">(</span>
  <span class="p">&lt;</span><span class="nc">View</span> <span class="na">style</span><span class="p">=</span><span class="si">{</span><span class="nx">styles</span><span class="p">.</span><span class="nx">card</span><span class="si">}</span><span class="p">&gt;</span>
    <span class="p">&lt;</span><span class="nc">Text</span> <span class="na">style</span><span class="p">=</span><span class="si">{</span><span class="nx">styles</span><span class="p">.</span><span class="nx">name</span><span class="si">}</span><span class="p">&gt;</span><span class="si">{</span><span class="nx">name</span><span class="si">}</span><span class="p">&lt;/</span><span class="nc">Text</span><span class="p">&gt;</span>
    <span class="p">&lt;</span><span class="nc">Text</span> <span class="na">style</span><span class="p">=</span><span class="si">{</span><span class="nx">styles</span><span class="p">.</span><span class="nx">age</span><span class="si">}</span><span class="p">&gt;</span>Age: <span class="si">{</span><span class="nx">age</span><span class="si">}</span><span class="p">&lt;/</span><span class="nc">Text</span><span class="p">&gt;</span>
  <span class="p">&lt;/</span><span class="nc">View</span><span class="p">&gt;</span>
<span class="p">);</span>

<span class="kd">const</span> <span class="nx">styles</span> <span class="o">=</span> <span class="nx">StyleSheet</span><span class="p">.</span><span class="nx">create</span><span class="p">({</span>
  <span class="na">card</span><span class="p">:</span> <span class="p">{</span>
    <span class="na">padding</span><span class="p">:</span> <span class="mi">16</span><span class="p">,</span>
    <span class="na">backgroundColor</span><span class="p">:</span> <span class="dl">'</span><span class="s1">#eee</span><span class="dl">'</span><span class="p">,</span>
    <span class="na">borderRadius</span><span class="p">:</span> <span class="mi">8</span><span class="p">,</span>
  <span class="p">},</span>
  <span class="na">name</span><span class="p">:</span> <span class="p">{</span>
    <span class="na">fontSize</span><span class="p">:</span> <span class="mi">18</span><span class="p">,</span>
    <span class="na">fontWeight</span><span class="p">:</span> <span class="dl">'</span><span class="s1">bold</span><span class="dl">'</span><span class="p">,</span>
  <span class="p">},</span>
  <span class="na">age</span><span class="p">:</span> <span class="p">{</span>
    <span class="na">fontSize</span><span class="p">:</span> <span class="mi">14</span><span class="p">,</span>
    <span class="na">color</span><span class="p">:</span> <span class="dl">'</span><span class="s1">#555</span><span class="dl">'</span><span class="p">,</span>
  <span class="p">},</span>
<span class="p">});</span>
</code></pre></div></div>

<p>The idea is the same. The syntax is different. Both are readable. Both are powerful.</p>

<h2 id="should-you-make-the-leap">Should you make the leap?</h2>

<p>If you’re ten years into iOS and feeling curious, the leap to React Native isn’t as scary as it seems. You won’t lose your skills—they’ll just evolve. Your SwiftUI and UIKit experience still helps you understand mobile constraints, UI expectations, app structure. But now, you get to see those same challenges from a different angle.</p>

<p>It’s humbling. It’s fun. It’s weird.</p>

<p>And sometimes, that’s exactly what your career needs.</p>]]></content><author><name>Mauricio Chirino</name></author><category term="coding" /><category term="coding" /><category term="swift" /><category term="engineering" /><category term="work" /><summary type="html"><![CDATA[Stepping into the dark side or finally peaking outside the Matrix?]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://geekingwithmauri.com/assets/resources/logo.png" /><media:content medium="image" url="https://geekingwithmauri.com/assets/resources/logo.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Swiftable 2023 - My First talk</title><link href="https://geekingwithmauri.com/swift/swiftable2023.html" rel="alternate" type="text/html" title="Swiftable 2023 - My First talk" /><published>2023-12-07T00:00:00+00:00</published><updated>2023-12-07T00:00:00+00:00</updated><id>https://geekingwithmauri.com/swift/swiftable2023</id><content type="html" xml:base="https://geekingwithmauri.com/swift/swiftable2023.html"><![CDATA[<!-- ------------ -->

<p style="text-align: center;"><img src="/assets/posts/17_swiftable/cover.jpeg" alt="dictionary" />
On stage at <a href="https://amigosdelbellasartes.org.ar">Amigos del Bellas Artes in Buenos Aires</a></p>

<blockquote>
  <p>If I can do it, you certainly can – me</p>
</blockquote>

<p>Recently I assisted to the <a href="https://www.swiftable.co">Swiftable 2023</a> conference in Buenos Aires. It’s not my first time there, my first one was <a href="https://www.swiftable.co/2022">December 2022</a>. Back then I went with a couple of friends from work.</p>

<p style="text-align: center;"><img src="/assets/posts/17_swiftable/swiftable22.jpg" alt="dictionary" /></p>
<p><em>With <a href="https://www.linkedin.com/in/federico-jordan">Federico “Fede” Jordan</a> and <a href="https://www.linkedin.com/in/franco-risma-49244849">Franco “Frisma” Risma</a> at <a href="https://turismo.buenosaires.gob.ar/es/otros-establecimientos/usina-del-arte">Usina del Arte</a></em></p>

<p>The difference this time though, is that <strong>I</strong> was in the <a href="https://www.swiftable.co/speakers/mauricio-chirino/">speaker’s roast</a>. It’s the first time I was speaking to a live audience of colleagues in English. In that regard, the classic “you’re the expert on X subject” didn’t hold true.</p>

<p>In this post I’ll share my journey in case someone else is thinking about entering in this arena. It’s fun as hell being on stage!</p>

<ul>
  <li><a href="#submission">Submission</a></li>
  <li><a href="#preparation">Preparation</a></li>
  <li><a href="#warm-up">Warm up</a></li>
  <li><a href="#the-day-of-the-event">The day of the event</a></li>
  <li><a href="#conclusion">Conclusion</a></li>
</ul>

<h2 id="submission">Submission</h2>

<p>The first thing was sending my proposal once the organizers (<a href="https://www.linkedin.com/in/facumenzella?">Facundo Menzella</a> and <a href="https://www.linkedin.com/in/josefinabustamante">Josefina Bustamente</a>) <a href="https://www.swiftable.co/cfp">opened the CFP</a>. I needed to expose, in fewer than 300 words, the topic of my talk. 
All I knew was that I wanted to talk about habits, the one thing <a href="/productivity/on-being-productive-with-endless-distractions.html">I’ve been studying for years now consistently</a> besides coding.</p>

<p>I laid out the concepts and gave the following <a href="https://www.papercall.io/speakers/mauri/speaker_talks/255204-atomic-coding-habits">elevator speech</a>:</p>

<blockquote>
  <p>The talk will consist of coding systems every developer should commit to, regardless of their seniority and type of job. Whether you’re a Jr on a big company or a Sr working for an emerging startup, these systems will make you more productive and overall avoid stressful situations in the long run.</p>
</blockquote>

<p>It did the trick because I got the gig 😎</p>

<h2 id="preparation">Preparation</h2>

<p>I knew I wanted to give my talk leveraging the animation power in Keynotes. However, all the animations in the toolkit won’t compensate for a lazy story. Therefore I needed some cohesive train of thought throughout I could be able to expand my ideas.</p>

<p>I decided to start with a problem based on my personal experience and take it from there. Also the title of my talk was <del>Atomic</del> Coding Habits so it made sense to use <a href="https://amzn.to/47aDr1m">the framework already provided by James Clear</a> to my advantage.</p>

<h2 id="warm-up">Warm up</h2>

<p>A week before the conference, I took it for a test drive with a couple of teammates. After giving my talk to them, the feedback I received was gold: it was a good talk but it didn’t resemble the concepts from the book:</p>

<ul>
  <li>The 4 laws of habits</li>
  <li>1% increments</li>
  <li>Make good habits easier</li>
  <li>Add friction to bad ones</li>
</ul>

<p>After hearing this I got two options: either cut my losses and patch up whatever set of slides I had managed to assemble already or take a hard left and restructure my talk. My wife simply told me: “you’ve got the time, it’s up to you”. And with that, I devoted the weekend previous to the trip to redo 80% of the keynote</p>

<h2 id="the-day-of-the-event">The day of the event</h2>

<p>A few things I did right that day:</p>

<ul>
  <li>
    <p><em>Scheduled ride</em>: any ride-hailing service has this feature. Granted, it’ll be a more expensive ride, but any bit of uncertainty avoided that day was a gift</p>
  </li>
  <li>
    <p><em>Took my own laptop</em>: The organizers offer a laptop for the presentation in case the speaker doesn’t bring their own. But then again, doubling down on certainty made me –and the rest of the speakers for that matter– bring my own equipment. You don’t want any unexpected surprise arising during the talk in a foreign device. (also, custom fonts 🎨)</p>
  </li>
  <li>
    <p><em>A good night sleep</em>: the day before the conference’s kick off we (the speakers) were invited to dinner where food and drinks were included. I stayed away from alcohol and too much sugar. That didn’t prevent me from enjoying myself and meet all sort of fellows from around the world there, I just didn’t go wild (there’d be time for that later in <a href="https://twitter.com/MChirino89/status/1731378112829460783/">the after party event</a>)</p>
  </li>
</ul>

<p style="text-align: center;"><img src="/assets/posts/17_swiftable/dinner.jpeg" alt="dictionary" />
With the <a href="https://www.emergetools.com">Emerge tools</a> fellows, the one and only <a href="https://www.swiftable.co/speakers/antoine-vanderlee/">Antoine “SwiftLee” Van Der Lee</a>, and <a href="https://www.swiftable.co/speakers/claudia-maciel/">Claudia “CoderPilot” Maciel</a></p>

<p>A couple of things I didn’t anticipate:</p>

<ul>
  <li><em>Light mode 💡:</em> The projector was adapted for light content and my slides were in dark mode. I heard <a href="https://www.swiftable.co/speakers/josh-holtz/">Josh Holtz</a> was able to quickly switch the entire theme of his presentation on spot but my slides consisted in many dark mode snapshots.</li>
  <li><em>Sonoma OS and permissions 🤦🏽‍♂️</em>: There was a video in one of my slides and Apple being Apple is now asking permissions for EVERYTHING the first time. Ergo, during my presentation the permission popup jumped and my presentation was interrupted. Learning: during prep test, go through <strong>the entire</strong> presentation at least once.</li>
</ul>

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

<p>Whatever the topic you’ve struggled with and come on the other side (either successfully or with a bunch of experience), there’s a talk waiting to happen somewhere in there. Just organize your thoughts in a story and practice. Chances are someone will appreciate those learning.</p>

<p>If you’d like to see the final result, <a href="https://www.youtube.com/watch?v=yDBZI9XK6Q4">here</a> it is 🫣</p>]]></content><author><name>Mauricio Chirino</name></author><category term="swift" /><category term="swift" /><category term="conference" /><category term="marketing" /><category term="engineering" /><category term="success" /><summary type="html"><![CDATA[From idea to talk. How's the process to become a speaker]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://geekingwithmauri.com/assets/resources/logo.png" /><media:content medium="image" url="https://geekingwithmauri.com/assets/resources/logo.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Tracking down effectiveness regarding the sharing feature on Swift</title><link href="https://geekingwithmauri.com/swift/sharingWithSwift.html" rel="alternate" type="text/html" title="Tracking down effectiveness regarding the sharing feature on Swift" /><published>2022-10-25T00:00:00+00:00</published><updated>2022-10-25T00:00:00+00:00</updated><id>https://geekingwithmauri.com/swift/sharingWithSwift</id><content type="html" xml:base="https://geekingwithmauri.com/swift/sharingWithSwift.html"><![CDATA[<!-- ------------ -->

<p style="text-align: center;"><img src="/assets/posts/16_sharingInSwift/cover.jpg" alt="dictionary" />
Image by <a href="https://www.pexels.com/@olly/">Andrea Piacquadio</a> from <a href="https://www.pexels.com/photo/cheerful-young-woman-screaming-into-megaphone-3761509/">Pexels</a></p>

<blockquote>
  <p>If you can’t measure it, you can’t improve it – unknown</p>
</blockquote>

<p>We cannot have a great product without some network component in it. When we talk about <em>network</em>, we’re not only referring to <a href="/swift/serverSideVapor.html">fetching data in a server</a> and pushing updates into it. We’re referring to word of mouth, sharing some cool feature/milestone with your close ones and even posting it on social media.</p>

<p>How do we embed this behavior within our iOS apps? Let’s see what we have for the menu this time:</p>

<ul>
  <li><a href="#first-part-of-the-equation">First part of the equation</a></li>
  <li><a href="#enter-tracking-to-the-picture">Enter tracking to the picture</a></li>
  <li><a href="#is-it-actually-working">Is it actually working?</a></li>
  <li><a href="#final-thoughts">Final thoughts</a></li>
</ul>

<h2 id="first-part-of-the-equation">First part of the equation</h2>

<p>UIKit provide us with the <code class="language-plaintext highlighter-rouge">UIActivityViewController</code> class to achieve precisely this.</p>

<figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">import</span> <span class="kt">UIKit</span>

<span class="kd">final</span> <span class="kd">class</span> <span class="kt">SocialMediaViewController</span><span class="p">:</span> <span class="kt">UIActivityViewController</span> <span class="p">{</span>
    <span class="nf">init</span><span class="p">(</span><span class="nv">qrImage</span><span class="p">:</span> <span class="kt">UIImage</span><span class="p">,</span> <span class="nv">catalogName</span><span class="p">:</span> <span class="kt">String</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">let</span> <span class="nv">message</span> <span class="o">=</span> <span class="s">"These are my missing stickers for </span><span class="se">\(</span><span class="n">catalogName</span><span class="se">)</span><span class="s">"</span>
        <span class="k">super</span><span class="o">.</span><span class="nf">init</span><span class="p">(</span><span class="nv">activityItems</span><span class="p">:</span> <span class="p">[</span><span class="n">message</span><span class="p">,</span> <span class="n">qrImage</span><span class="p">],</span> <span class="nv">applicationActivities</span><span class="p">:</span> <span class="kc">nil</span><span class="p">)</span>
        <span class="n">excludedActivityTypes</span> <span class="o">=</span> <span class="p">[</span><span class="o">.</span><span class="n">assignToContact</span><span class="p">,</span>
                                 <span class="o">.</span><span class="n">addToReadingList</span><span class="p">,</span>
                                 <span class="o">.</span><span class="n">markupAsPDF</span><span class="p">,</span>
                                 <span class="o">.</span><span class="n">openInIBooks</span><span class="p">,</span>
                                 <span class="o">.</span><span class="n">postToFlickr</span><span class="p">,</span>
                                 <span class="o">.</span><span class="n">print</span><span class="p">,</span>
                                 <span class="o">.</span><span class="n">saveToCameraRoll</span><span class="p">]</span>
    <span class="p">}</span>
<span class="p">}</span></code></pre></figure>

<p>The above snippet is extracted from the sharing QR funnel in <a href="https://apps.apple.com/uy/app/mystickers/id884352968">MyStickers</a>. Now it is as simple as instantiating a <code class="language-plaintext highlighter-rouge">SocialMediaViewController</code> and presenting it.</p>

<h2 id="enter-tracking-to-the-picture">Enter tracking to the picture</h2>

<p>As solo developers in a bootstrap project, we often need to make choices related to focus. The beautiful thing about coding (for those of us who love it) is that it’s an endless endeavor. However, at the end of the day all that code matters only <em>if</em> it’s actually being used. How do we know our sharing popup is actually being shown?</p>

<p>We need to track it down. Let’s suppose we’re using the <a href="https://github.com/GeekingwithMauri/TrackingEngine">TrackingEngine</a> package to achieve this. We could log an event in our Coordinator whenever the <code class="language-plaintext highlighter-rouge">SocialMediaViewController</code> is presented like so 👇🏽</p>

<figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">func</span> <span class="nf">launchSharing</span><span class="p">(</span><span class="n">with</span> <span class="nv">qrImage</span><span class="p">:</span> <span class="kt">UIImage</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">let</span> <span class="nv">socialSharing</span> <span class="o">=</span> <span class="kt">SocialMediaViewController</span><span class="p">(</span><span class="nv">qrImage</span><span class="p">:</span> <span class="n">qrImage</span><span class="p">,</span> <span class="nv">catalogName</span><span class="p">:</span> <span class="s">"Qatar 2022"</span><span class="p">)</span>

    <span class="n">rootViewController</span><span class="o">.</span><span class="nf">present</span><span class="p">(</span><span class="n">viewControllerToPresent</span><span class="p">,</span> <span class="nv">animated</span><span class="p">:</span> <span class="kc">true</span><span class="p">)</span> <span class="p">{</span> <span class="p">[</span><span class="k">weak</span> <span class="k">self</span><span class="p">]</span> <span class="k">in</span>
        <span class="k">self</span><span class="p">?</span><span class="o">.</span><span class="n">tracker</span><span class="o">.</span><span class="nf">track</span><span class="p">(</span>
            <span class="nv">eventName</span><span class="p">:</span> <span class="s">"share_initiated"</span><span class="p">,</span>
            <span class="nv">parameters</span><span class="p">:</span> <span class="p">[</span><span class="s">"name"</span><span class="p">:</span> <span class="n">storageController</span><span class="o">.</span><span class="n">currentCatalogueName</span><span class="p">]</span>
        <span class="p">)</span>
    <span class="p">}</span>
<span class="p">}</span></code></pre></figure>

<p>Now we can track whenever our users use <em>started</em> the sharing feature…</p>

<h2 id="is-it-actually-working">Is it actually working?</h2>
<blockquote>
  <p>Garbage in, garbage out</p>
</blockquote>

<p>But wait, this only tracks when the users <em>initiated</em> the sharing funnel. It doesn’t actually track when they fulfill it. Our statistics and measures are only as sound as the input we feed them with. Let’s be more precise.</p>

<p>The <code class="language-plaintext highlighter-rouge">UIActivityViewController</code> has an optional typealias called <a href="[https://developer.apple.com/documentation/uikit/uiactivityviewcontroller/completionwithitemshandler]"><code class="language-plaintext highlighter-rouge">completionWithItemsHandler</code></a> which is a closure that gets executed whenever the activity itself was fulfilled (or <strong>dismissed</strong>). Said closure expects 4 parameters:</p>

<ul>
  <li>activityType (<code class="language-plaintext highlighter-rouge">UIActivity.ActivityType?</code>): it refers to the type of service selected by the user. This could be useful to track down which platforms are converting more and put more efforts into customizing said flows.</li>
  <li>completed (<code class="language-plaintext highlighter-rouge">Bool</code>): The only moment when this is <code class="language-plaintext highlighter-rouge">true</code> is when the user actually completed the sharing itself.</li>
  <li>returnedItems (<code class="language-plaintext highlighter-rouge">Any?</code>): For custom activities that involve modifying data by its extensions, this value is useful. It returns any changes made to the original data.</li>
  <li>activityError (<code class="language-plaintext highlighter-rouge">Error?</code>): if something went wrong, this value will tell us what was it. In case the activity completed smoothly, it’ll be <code class="language-plaintext highlighter-rouge">nil</code></li>
</ul>

<p>With all this context we can now track more confidently our flow</p>

<figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">func</span> <span class="nf">launchSharing</span><span class="p">(</span><span class="n">with</span> <span class="nv">qrImage</span><span class="p">:</span> <span class="kt">UIImage</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">let</span> <span class="nv">socialSharing</span> <span class="o">=</span> <span class="kt">SocialMediaViewController</span><span class="p">(</span><span class="nv">qrImage</span><span class="p">:</span> <span class="n">qrImage</span><span class="p">,</span> <span class="nv">catalogName</span><span class="p">:</span> <span class="s">"Qatar 2022"</span><span class="p">)</span>

    <span class="n">socialSharing</span><span class="o">.</span><span class="n">completionWithItemsHandler</span> <span class="o">=</span> <span class="p">{</span> <span class="p">[</span><span class="k">weak</span> <span class="k">self</span><span class="p">]</span> <span class="n">item</span><span class="p">,</span> <span class="n">completed</span><span class="p">,</span> <span class="n">values</span><span class="p">,</span> <span class="n">error</span> <span class="k">in</span>
         <span class="k">self</span><span class="p">?</span><span class="o">.</span><span class="nf">trackSharing</span><span class="p">(</span><span class="nv">itemType</span><span class="p">:</span> <span class="n">item</span><span class="p">,</span> 
                            <span class="nv">wasSharingCompleted</span><span class="p">:</span> <span class="n">completed</span><span class="p">,</span> 
                            <span class="nv">values</span><span class="p">:</span> <span class="n">values</span><span class="p">,</span>
                            <span class="nv">receivedErrors</span><span class="p">:</span> <span class="n">error</span><span class="p">)</span>
    <span class="p">}</span>

    <span class="n">rootViewController</span><span class="o">.</span><span class="nf">present</span><span class="p">(</span><span class="n">viewControllerToPresent</span><span class="p">,</span> <span class="nv">animated</span><span class="p">:</span> <span class="kc">true</span><span class="p">)</span> <span class="p">{</span> <span class="p">[</span><span class="k">weak</span> <span class="k">self</span><span class="p">]</span> <span class="k">in</span>
        <span class="c1">// Anonymous closure executed after the `present` action is done</span>
        <span class="k">self</span><span class="p">?</span><span class="o">.</span><span class="n">tracker</span><span class="o">.</span><span class="nf">track</span><span class="p">(</span>
            <span class="nv">eventName</span><span class="p">:</span> <span class="s">"share_initiated"</span><span class="p">,</span>
            <span class="nv">parameters</span><span class="p">:</span> <span class="p">[</span><span class="s">"name"</span><span class="p">:</span> <span class="n">storageController</span><span class="o">.</span><span class="n">currentCatalogueName</span><span class="p">]</span>
        <span class="p">)</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="kd">func</span> <span class="nf">trackSharing</span><span class="p">(</span><span class="nv">itemType</span><span class="p">:</span> <span class="kt">UIActivity</span><span class="o">.</span><span class="kt">ActivityType</span><span class="p">?,</span>
                  <span class="nv">wasSharingCompleted</span><span class="p">:</span> <span class="kt">Bool</span><span class="p">,</span>
                  <span class="nv">values</span><span class="p">:</span> <span class="p">[</span><span class="kt">Any</span><span class="p">]?,</span>
                  <span class="nv">receivedErrors</span><span class="p">:</span> <span class="kt">Error</span><span class="p">?)</span> <span class="p">{</span>
        <span class="k">guard</span> <span class="n">wasSharingCompleted</span> <span class="k">else</span> <span class="p">{</span>
            <span class="c1">// Sharing action got cancelled by the user 😕</span>
            <span class="n">tracker</span><span class="o">.</span><span class="nf">track</span><span class="p">(</span><span class="nv">eventName</span><span class="p">:</span> <span class="s">"share_canceled"</span><span class="p">,</span> <span class="nv">parameters</span><span class="p">:</span> <span class="kc">nil</span><span class="p">)</span>
            <span class="k">return</span>
        <span class="p">}</span>

        <span class="k">var</span> <span class="nv">parameters</span><span class="p">:</span> <span class="p">[</span><span class="kt">String</span><span class="p">:</span> <span class="kt">Any</span><span class="p">]</span> <span class="o">=</span> <span class="p">[</span><span class="s">"method"</span><span class="p">:</span> <span class="n">itemType</span><span class="p">?</span><span class="o">.</span><span class="n">rawValue</span> <span class="p">??</span> <span class="s">"N/A"</span><span class="p">]</span>

        <span class="k">if</span> <span class="k">let</span> <span class="nv">shareError</span> <span class="o">=</span> <span class="n">receivedErrors</span><span class="p">?</span><span class="o">.</span><span class="n">localizedDescription</span> <span class="p">{</span>
            <span class="c1">// Something went wrong and we track down exactly what 🧐</span>
            <span class="n">parameters</span><span class="p">[</span><span class="s">"error"</span><span class="p">]</span> <span class="o">=</span> <span class="n">shareError</span>
            <span class="n">tracker</span><span class="o">.</span><span class="nf">track</span><span class="p">(</span><span class="nv">eventName</span><span class="p">:</span> <span class="s">"share_failed"</span><span class="p">,</span> <span class="nv">parameters</span><span class="p">:</span> <span class="n">parameters</span><span class="p">)</span>
            <span class="k">return</span>
        <span class="p">}</span>

        <span class="c1">// Jackpot! The user went through the entire funnel 🥳</span>
        <span class="n">tracker</span><span class="o">.</span><span class="nf">track</span><span class="p">(</span><span class="nv">eventName</span><span class="p">:</span> <span class="s">"share_executed_successfully"</span><span class="p">,</span> <span class="nv">parameters</span><span class="p">:</span> <span class="n">parameters</span><span class="p">)</span>
    <span class="p">}</span>
<span class="p">}</span></code></pre></figure>

<h2 id="final-thoughts">Final thoughts</h2>
<p>There are countless reasons why our users might not go all the way through the sharing funnels. Just to mention a few of then:</p>

<ul>
  <li>An incoming call and later forgetting to return to the app</li>
  <li>The app crashing whenever the sharing started feature gets started</li>
  <li>The method they chose isn’t is fully supported by our apps yet</li>
</ul>

<p>Only by careful tracking each steps of their journey can we hone in the data and tweak our assumptions to match their reality, thus crafting an optimal UX for them.</p>]]></content><author><name>Mauricio Chirino</name></author><category term="swift" /><category term="swift" /><category term="uikit" /><category term="marketing" /><category term="engineering" /><category term="productivity" /><category term="success" /><category term="framework" /><summary type="html"><![CDATA[How can we be certain that people are sharing our apps?]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://geekingwithmauri.com/assets/resources/logo.png" /><media:content medium="image" url="https://geekingwithmauri.com/assets/resources/logo.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Guarantee localization with SwitfUI</title><link href="https://geekingwithmauri.com/swift/localizationWithSwiftUI.html" rel="alternate" type="text/html" title="Guarantee localization with SwitfUI" /><published>2021-11-08T00:00:00+00:00</published><updated>2021-11-08T00:00:00+00:00</updated><id>https://geekingwithmauri.com/swift/localizationWithSwiftUI</id><content type="html" xml:base="https://geekingwithmauri.com/swift/localizationWithSwiftUI.html"><![CDATA[<!-- ------------ -->

<p style="text-align: center;"><img src="/assets/posts/15_swiftUITesting/cover.jpg" alt="dictionary" />
Image by <a href="https://www.pexels.com/@karolina-grabowska?utm_content=attributionCopyText&amp;utm_medium=referral&amp;utm_source=pexels">Karolina Grabowska</a> from <a href="https://www.pexels.com/photo/person-wearing-silver-ring-holding-white-book-page-5238117/?utm_content=attributionCopyText&amp;utm_medium=referral&amp;utm_source=pexels">Pixabay</a></p>

<p>Manually testing our UI in search of localization bugs is a ridiculous thing to do in 2021. So what is it then? Why do we keep coming back to the same topic?</p>

<ul>
  <li><a href="#prerequisites">Prerequisites</a></li>
  <li><a href="#case-study">Case study</a></li>
  <li><a href="#the-problem">The problem</a></li>
  <li><a href="#snapshot-tests">Snapshot tests</a></li>
  <li><a href="#proper-localization">Proper localization</a></li>
  <li><a href="#test-plans">Test plans</a></li>
  <li><a href="#final-thoughts">Final thoughts</a></li>
</ul>

<h2 id="prerequisites">Prerequisites</h2>

<p>The topic we’re about to discuss assumes a few things from your part in order to get the most of it such as basic knowledge of:</p>

<ul>
  <li>
    <p><em>SwiftUI</em>: it is the most recent UI framework from Apple. It allows us to build in a declarative fashion instead of good old imperative’s UIKit way has all of us used to. This is the first time I’m talking about it in this blog but there are countless resources out there to learn from. I won’t get into too many details.</p>
  </li>
  <li>
    <p><em>Localization</em>: Making our app be readable in multiple languages was covered in a [previous post][localizable]. Apple provides some tools to do this without too much hustle so again: details will be scarce.</p>
  </li>
  <li>
    <p><em>Snapshot testing</em>: I talked in detail in a previous <a href="https://medium.com/peya-tech/screenshots-para-tus-tests-85598e4c5c4e">medium post</a> (sorry but that one it’s on Spanish only, let me know via Twitter if this is something you’d like me to go in detail). It’s simply the practice of saving screenshots of our UI’s different state in order to later compare them and be notified when something changes.</p>
  </li>
</ul>

<h2 id="case-study">Case study</h2>

<p>Let’s say we want to draw a simple list of steps for one hypothetical audio edition/publishing app.</p>

<figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="c1">//</span>
<span class="c1">//  InstructionsView.swift</span>
<span class="c1">//</span>
<span class="c1">//  Created by Mauricio Chirino on 23/10/21.</span>
<span class="c1">//</span>

<span class="kd">import</span> <span class="kt">SwiftUI</span>

<span class="kd">struct</span> <span class="kt">ItemRow</span><span class="p">:</span> <span class="kt">View</span> <span class="p">{</span>
    <span class="k">let</span> <span class="nv">stepData</span><span class="p">:</span> <span class="kt">String</span>
    <span class="k">var</span> <span class="nv">body</span><span class="p">:</span> <span class="kd">some</span> <span class="kt">View</span> <span class="p">{</span>
        <span class="kt">HStack</span> <span class="p">{</span>
            <span class="k">let</span> <span class="nv">icon</span> <span class="o">=</span> <span class="kt">String</span><span class="p">(</span><span class="n">stepData</span><span class="o">.</span><span class="n">last</span> <span class="p">??</span> <span class="kt">Character</span><span class="p">(</span><span class="s">""</span><span class="p">))</span>
            <span class="k">let</span> <span class="nv">name</span> <span class="o">=</span> <span class="n">stepData</span><span class="o">.</span><span class="nf">dropLast</span><span class="p">()</span>
            <span class="kt">Text</span><span class="p">(</span><span class="n">name</span><span class="p">)</span>
            <span class="kt">Spacer</span><span class="p">()</span>
            <span class="kt">Text</span><span class="p">(</span><span class="n">icon</span><span class="p">)</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="kd">struct</span> <span class="kt">StepsModel</span> <span class="p">{</span>
    <span class="k">let</span> <span class="nv">dataSource</span><span class="p">:</span> <span class="p">[</span><span class="kt">String</span><span class="p">]</span> <span class="o">=</span> <span class="p">[</span>
        <span class="s">"Select audio file 🎶"</span><span class="p">,</span>
        <span class="s">"Polish it 🦻🏽"</span><span class="p">,</span>
        <span class="s">"Publish it 📢"</span>
    <span class="p">]</span>
<span class="p">}</span>

<span class="kd">struct</span> <span class="kt">ContentView</span><span class="p">:</span> <span class="kt">View</span> <span class="p">{</span>
    <span class="k">let</span> <span class="nv">stepsDataSource</span><span class="p">:</span> <span class="p">[</span><span class="kt">String</span><span class="p">]</span>

    <span class="k">var</span> <span class="nv">body</span><span class="p">:</span> <span class="kd">some</span> <span class="kt">View</span> <span class="p">{</span>
        <span class="kt">VStack</span> <span class="p">{</span>
            <span class="kt">Text</span><span class="p">(</span><span class="s">"Steps!"</span><span class="p">)</span><span class="o">.</span><span class="nf">font</span><span class="p">(</span><span class="o">.</span><span class="n">title2</span><span class="p">)</span><span class="o">.</span><span class="nf">bold</span><span class="p">()</span>
            <span class="kt">List</span> <span class="p">{</span>
                <span class="kt">ForEach</span><span class="p">(</span><span class="n">stepsDataSource</span><span class="p">,</span> <span class="nv">id</span><span class="p">:</span> <span class="p">\</span><span class="o">.</span><span class="k">self</span><span class="p">)</span> <span class="p">{</span> <span class="n">stepData</span> <span class="k">in</span>
                    <span class="kt">ItemRow</span><span class="p">(</span><span class="nv">stepData</span><span class="p">:</span> <span class="n">stepData</span><span class="p">)</span>
                <span class="p">}</span>
            <span class="p">}</span>
        <span class="p">}</span><span class="o">.</span><span class="nf">background</span><span class="p">(</span><span class="kt">Color</span><span class="p">(</span><span class="o">.</span><span class="n">systemBackground</span><span class="p">))</span>
    <span class="p">}</span>
<span class="p">}</span></code></pre></figure>

<p>This bit of code is enough to render this screen below</p>

<p style="text-align: center;"><img src="https://geekingwithmauri.com/assets/posts/15_swiftUITesting/render.png" alt="rendered UI" /></p>

<p>If you’re an old timer like me you might be wondering about <code class="language-plaintext highlighter-rouge">UITableViewDataSource</code>/<code class="language-plaintext highlighter-rouge">UICollectionViewDataSource</code>, <em>AutoLayout</em> or at least the <code class="language-plaintext highlighter-rouge">ViewController</code>. SwiftUI takes care of that, demanding from us only declaration of <em>what</em> has to be rendered, not <em>how</em>. Amazing, right?</p>

<p>What about a preview of our UI in different devices in light and dark mode? SwiftUI also got us covered there 👇🏽</p>

<figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="c1">//</span>
<span class="c1">//  Preview.swift</span>
<span class="c1">//</span>
<span class="c1">//  Created by Mauricio Chirino on 24/10/21.</span>
<span class="c1">//</span>

<span class="kd">import</span> <span class="kt">SwiftUI</span>

<span class="kd">struct</span> <span class="kt">ContentView_Previews</span><span class="p">:</span> <span class="kt">PreviewProvider</span> <span class="p">{</span>
    <span class="kd">static</span> <span class="k">var</span> <span class="nv">previews</span><span class="p">:</span> <span class="kd">some</span> <span class="kt">View</span> <span class="p">{</span>
        <span class="k">let</span> <span class="nv">devices</span><span class="p">:</span> <span class="p">[</span><span class="kt">String</span><span class="p">]</span> <span class="o">=</span> <span class="p">[</span>
            <span class="s">"iPhone 8"</span><span class="p">,</span>
            <span class="s">"iPhone 11 Pro Max"</span><span class="p">,</span>
            <span class="s">"iPhone 13 mini"</span>
        <span class="p">]</span>

        <span class="kt">Group</span> <span class="p">{</span>
            <span class="kt">ForEach</span><span class="p">(</span><span class="n">devices</span><span class="p">,</span> <span class="nv">id</span><span class="p">:</span> <span class="p">\</span><span class="o">.</span><span class="k">self</span><span class="p">)</span> <span class="p">{</span> <span class="n">name</span> <span class="k">in</span>
                <span class="kt">ForEach</span><span class="p">(</span><span class="kt">ColorScheme</span><span class="o">.</span><span class="n">allCases</span><span class="p">,</span> <span class="nv">id</span><span class="p">:</span> <span class="p">\</span><span class="o">.</span><span class="k">self</span><span class="p">)</span> <span class="p">{</span> <span class="n">mode</span> <span class="k">in</span>
                    <span class="kt">ContentView</span><span class="p">(</span><span class="nv">stepsDataSource</span><span class="p">:</span> <span class="kt">StepsModel</span><span class="p">()</span><span class="o">.</span><span class="n">dataSource</span><span class="p">)</span>
                        <span class="o">.</span><span class="nf">previewDevice</span><span class="p">(</span><span class="kt">PreviewDevice</span><span class="p">(</span><span class="nv">rawValue</span><span class="p">:</span> <span class="n">name</span><span class="p">))</span>
                        <span class="o">.</span><span class="nf">previewDisplayName</span><span class="p">(</span><span class="n">name</span><span class="p">)</span>
                        <span class="o">.</span><span class="nf">environment</span><span class="p">(\</span><span class="o">.</span><span class="n">colorScheme</span><span class="p">,</span> <span class="n">mode</span><span class="p">)</span>
                <span class="p">}</span>
            <span class="p">}</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span></code></pre></figure>

<p>As a side note, running the following command on a terminal will print out all available simulators in our environment.</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="nv">$ </span>xcrun simctl list devicetypes</code></pre></figure>
<hr />
<hr />

<table>
  <tbody>
    <tr>
      <td><img src="https://geekingwithmauri.com/assets/posts/15_swiftUITesting/8_light.png" alt="iPhone 8" /></td>
      <td><img src="https://geekingwithmauri.com/assets/posts/15_swiftUITesting/11_light.png" alt="iPhone 11 Pro Max" /></td>
      <td><img src="https://geekingwithmauri.com/assets/posts/15_swiftUITesting/13_light.png" alt="iPhone 13 mini" /></td>
    </tr>
  </tbody>
</table>

<table>
  <tbody>
    <tr>
      <td><img src="https://geekingwithmauri.com/assets/posts/15_swiftUITesting/8_dark.png" alt="iPhone 8" /></td>
      <td><img src="https://geekingwithmauri.com/assets/posts/15_swiftUITesting/11_dark.png" alt="iPhone 11 Pro Max" /></td>
      <td><img src="https://geekingwithmauri.com/assets/posts/15_swiftUITesting/13_dark.png" alt="iPhone 13 mini" /></td>
    </tr>
  </tbody>
</table>

<p>All of the above was rendered alongside my code within Xcode as I was tweaking the code. A real game changer regarding time saving and productivity 🎉</p>

<h2 id="the-problem">The problem</h2>

<p>“I don’t see the problem Mauri 🧐” you might be thinking. The above is all fine and dandy for small/prototype/side projects. The real issue arises with scale: what about multiple languages? Orientations (landscape/portrait)? Light and dark mode? Checking that no regressions are produce as the codebase grows? We need to introduce some automation in here!</p>

<h2 id="snapshot-tests">Snapshot tests</h2>
<p>This is where snapshot tests come in handy. In a nutshell: they take a screenshot of the device with the setup we provide and store it for later comparison. As soon as something in the UI changes without us intending to do so, said test would immediately fail (also providing a new screenshot for us to check what changed).</p>

<p>Let’s say we want to test in 3 different types of devices (an iPhone 8, 8+ and 12 Pro Max) for both light and dark mode, as well as landscape and portrait orientations.</p>

<figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">import</span> <span class="kt">SnapshotTesting</span>
<span class="kd">import</span> <span class="kt">SwiftUI</span>
<span class="kd">import</span> <span class="kt">XCTest</span>
<span class="kd">@testable</span> <span class="kd">import</span> <span class="kt">LocalizationSwiftUI</span>

<span class="kd">final</span> <span class="kd">class</span> <span class="kt">LocalizationSwiftUISnapshotTests</span><span class="p">:</span> <span class="kt">XCTestCase</span> <span class="p">{</span>
    <span class="kd">func</span> <span class="nf">verifyRendering</span><span class="p">(</span><span class="k">for</span> <span class="nv">device</span><span class="p">:</span> <span class="kt">ViewImageConfig</span><span class="p">,</span> <span class="nv">style</span><span class="p">:</span> <span class="kt">UIUserInterfaceStyle</span> <span class="o">=</span> <span class="o">.</span><span class="n">dark</span><span class="p">,</span> <span class="nv">testName</span><span class="p">:</span> <span class="kt">String</span><span class="p">)</span> <span class="p">{</span>
        <span class="c1">// Given</span>
        <span class="k">let</span> <span class="nv">sut</span> <span class="o">=</span> <span class="kt">ContentView</span><span class="p">(</span><span class="nv">stepsDataSource</span><span class="p">:</span> <span class="kt">StepsModel</span><span class="p">()</span><span class="o">.</span><span class="n">dataSource</span><span class="p">)</span>

        <span class="c1">// When</span>
        <span class="k">let</span> <span class="nv">wrapper</span> <span class="o">=</span> <span class="kt">UIHostingController</span><span class="p">(</span><span class="nv">rootView</span><span class="p">:</span> <span class="n">sut</span><span class="p">)</span>
        <span class="n">wrapper</span><span class="o">.</span><span class="n">overrideUserInterfaceStyle</span> <span class="o">=</span> <span class="n">style</span>

        <span class="c1">// Verify</span>
        <span class="nf">assertSnapshot</span><span class="p">(</span><span class="nv">matching</span><span class="p">:</span> <span class="n">wrapper</span><span class="p">,</span> <span class="nv">as</span><span class="p">:</span> <span class="o">.</span><span class="nf">image</span><span class="p">(</span><span class="nv">on</span><span class="p">:</span> <span class="n">device</span><span class="p">),</span> <span class="nv">testName</span><span class="p">:</span> <span class="n">testName</span><span class="p">)</span>
    <span class="p">}</span>
    <span class="kd">func</span> <span class="nf">test_initialRendering_forEnglishLocaleAtDarkMode</span><span class="p">()</span> <span class="p">{</span>
        <span class="nf">verifyRendering</span><span class="p">(</span><span class="nv">for</span><span class="p">:</span> <span class="o">.</span><span class="n">iPhone8</span><span class="p">,</span> <span class="nv">testName</span><span class="p">:</span> <span class="kd">#function</span><span class="p">)</span>
        <span class="nf">verifyRendering</span><span class="p">(</span><span class="nv">for</span><span class="p">:</span> <span class="o">.</span><span class="n">iPhone8Plus</span><span class="p">,</span> <span class="nv">testName</span><span class="p">:</span> <span class="kd">#function</span><span class="p">)</span>
        <span class="nf">verifyRendering</span><span class="p">(</span><span class="nv">for</span><span class="p">:</span> <span class="o">.</span><span class="n">iPhone12ProMax</span><span class="p">,</span> <span class="nv">testName</span><span class="p">:</span> <span class="kd">#function</span><span class="p">)</span>
        <span class="nf">verifyRendering</span><span class="p">(</span><span class="nv">for</span><span class="p">:</span> <span class="o">.</span><span class="nf">iPhone8</span><span class="p">(</span><span class="o">.</span><span class="n">landscape</span><span class="p">),</span> <span class="nv">testName</span><span class="p">:</span> <span class="kd">#function</span><span class="p">)</span>
        <span class="nf">verifyRendering</span><span class="p">(</span><span class="nv">for</span><span class="p">:</span> <span class="o">.</span><span class="nf">iPhone8Plus</span><span class="p">(</span><span class="o">.</span><span class="n">landscape</span><span class="p">),</span> <span class="nv">testName</span><span class="p">:</span> <span class="kd">#function</span><span class="p">)</span>
        <span class="nf">verifyRendering</span><span class="p">(</span><span class="nv">for</span><span class="p">:</span> <span class="o">.</span><span class="nf">iPhone12ProMax</span><span class="p">(</span><span class="o">.</span><span class="n">landscape</span><span class="p">),</span> <span class="nv">testName</span><span class="p">:</span> <span class="kd">#function</span><span class="p">)</span>
    <span class="p">}</span>

    <span class="kd">func</span> <span class="nf">test_initialRendering_forEnglishLocaleAtLightMode</span><span class="p">()</span> <span class="p">{</span>
        <span class="nf">verifyRendering</span><span class="p">(</span><span class="nv">for</span><span class="p">:</span> <span class="o">.</span><span class="n">iPhone8</span><span class="p">,</span> <span class="nv">style</span><span class="p">:</span> <span class="o">.</span><span class="n">light</span><span class="p">,</span> <span class="nv">testName</span><span class="p">:</span> <span class="kd">#function</span><span class="p">)</span>
        <span class="nf">verifyRendering</span><span class="p">(</span><span class="nv">for</span><span class="p">:</span> <span class="o">.</span><span class="n">iPhone8Plus</span><span class="p">,</span> <span class="nv">style</span><span class="p">:</span> <span class="o">.</span><span class="n">light</span><span class="p">,</span> <span class="nv">testName</span><span class="p">:</span> <span class="kd">#function</span><span class="p">)</span>
        <span class="nf">verifyRendering</span><span class="p">(</span><span class="nv">for</span><span class="p">:</span> <span class="o">.</span><span class="n">iPhone12ProMax</span><span class="p">,</span> <span class="nv">style</span><span class="p">:</span> <span class="o">.</span><span class="n">light</span><span class="p">,</span> <span class="nv">testName</span><span class="p">:</span> <span class="kd">#function</span><span class="p">)</span>
        <span class="nf">verifyRendering</span><span class="p">(</span><span class="nv">for</span><span class="p">:</span> <span class="o">.</span><span class="nf">iPhone8</span><span class="p">(</span><span class="o">.</span><span class="n">landscape</span><span class="p">),</span> <span class="nv">style</span><span class="p">:</span> <span class="o">.</span><span class="n">light</span><span class="p">,</span> <span class="nv">testName</span><span class="p">:</span> <span class="kd">#function</span><span class="p">)</span>
        <span class="nf">verifyRendering</span><span class="p">(</span><span class="nv">for</span><span class="p">:</span> <span class="o">.</span><span class="nf">iPhone8Plus</span><span class="p">(</span><span class="o">.</span><span class="n">landscape</span><span class="p">),</span> <span class="nv">style</span><span class="p">:</span> <span class="o">.</span><span class="n">light</span><span class="p">,</span> <span class="nv">testName</span><span class="p">:</span> <span class="kd">#function</span><span class="p">)</span>
        <span class="nf">verifyRendering</span><span class="p">(</span><span class="nv">for</span><span class="p">:</span> <span class="o">.</span><span class="nf">iPhone12ProMax</span><span class="p">(</span><span class="o">.</span><span class="n">landscape</span><span class="p">),</span> <span class="nv">style</span><span class="p">:</span> <span class="o">.</span><span class="n">light</span><span class="p">,</span> <span class="nv">testName</span><span class="p">:</span> <span class="kd">#function</span><span class="p">)</span>
    <span class="p">}</span>
<span class="p">}</span></code></pre></figure>

<p>The first time these tests are run, they failed because there’s no a single anchor image to make comparisons with. In this instance, all snapshots will be created and afterwards will fail only when something from the UI changes. The resulting anchors will sit next to our tests files inside our project</p>

<p><img src="https://geekingwithmauri.com/assets/posts/15_swiftUITesting/anchors.png" alt="anchor images" /></p>

<h2 id="proper-localization">Proper localization</h2>
<p>What if we need supporting another language? Let’s say Spanish to make things easy for the writer 😁🇻🇪</p>

<p>First of all, let’s replace the hard coded strings by keys from the language tables we’re adding for both English and Spanish, and access them using the same technique discussed in the <a href="/swift/Self-descriptive-localizable.html">localization post</a></p>

<figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="c1">/// Protocol intended to provide a key string localized for its respective value</span>
<span class="kd">protocol</span> <span class="kt">Localizable</span> <span class="p">{</span>
    <span class="c1">/// Returns localized value should it be found defined with this matching key</span>
    <span class="k">var</span> <span class="nv">key</span><span class="p">:</span> <span class="kt">String</span> <span class="p">{</span> <span class="k">get</span> <span class="p">}</span>
<span class="p">}</span>

<span class="kd">extension</span> <span class="kt">Localizable</span> <span class="k">where</span> <span class="k">Self</span><span class="p">:</span> <span class="kt">RawRepresentable</span><span class="p">,</span> <span class="k">Self</span><span class="o">.</span><span class="kt">RawValue</span> <span class="o">==</span> <span class="kt">String</span> <span class="p">{</span>
    <span class="k">var</span> <span class="nv">key</span><span class="p">:</span> <span class="kt">String</span> <span class="p">{</span>
        <span class="kt">NSLocalizedString</span><span class="p">(</span><span class="n">rawValue</span><span class="p">,</span> <span class="nv">comment</span><span class="p">:</span> <span class="s">""</span><span class="p">)</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="kd">enum</span> <span class="kt">Translator</span><span class="p">:</span> <span class="kt">String</span><span class="p">,</span> <span class="kt">Localizable</span> <span class="p">{</span>
    <span class="k">case</span> <span class="n">title</span>
    <span class="k">case</span> <span class="n">select</span>
    <span class="k">case</span> <span class="n">polish</span>
    <span class="k">case</span> <span class="n">publish</span>
<span class="p">}</span>

<span class="kd">struct</span> <span class="kt">StepsModel</span> <span class="p">{</span>
    <span class="k">let</span> <span class="nv">dataSource</span><span class="p">:</span> <span class="p">[</span><span class="kt">String</span><span class="p">]</span> <span class="o">=</span> <span class="p">[</span>
        <span class="kt">Translator</span><span class="o">.</span><span class="n">select</span><span class="o">.</span><span class="n">key</span><span class="p">,</span>
        <span class="kt">Translator</span><span class="o">.</span><span class="n">polish</span><span class="o">.</span><span class="n">key</span><span class="p">,</span>
        <span class="kt">Translator</span><span class="o">.</span><span class="n">publish</span><span class="o">.</span><span class="n">key</span>
    <span class="p">]</span>
<span class="p">}</span></code></pre></figure>

<p>Let’s see how it looks setting a device in Spanish:</p>

<table>
  <tbody>
    <tr>
      <td><img src="https://geekingwithmauri.com/assets/posts/15_swiftUITesting/deviceOnEnglish.png" alt="Device in English" /></td>
      <td><img src="https://geekingwithmauri.com/assets/posts/15_swiftUITesting/deviceOnSpanish.png" alt="Device in Spanish" /></td>
    </tr>
  </tbody>
</table>

<p>Thanks to the test we added in the previous part, we are confident this change isn’t causing any regression. This is what a I call <a href="/work/refactoring.html">a proper refactor</a>! 👏🏽</p>

<h2 id="test-plans">Test plans</h2>

<p>This is only half of the solution in our scenario. Snapshots until this point are going to test in a single environment configuration. Having to manually setup a device or simulator in a specific context and then running a set of specifics test for it defeats the purpose of automation.</p>

<p>Enter <em>test plans</em> into the picture: they were introduced in WWDC 2019 with Xcode 11 as a way to organize our test environment. Let’s setup a test suite to cover both languages.</p>

<ul>
  <li>We go to Product -&gt; Scheme -&gt; Edit scheme in order to convert our test suite in a test plan</li>
</ul>

<p><img src="https://geekingwithmauri.com/assets/posts/15_swiftUITesting/testPlan/1.jpg" alt="test plan in the making" /></p>

<ul>
  <li>Create a test plan from current scheme</li>
</ul>

<p style="text-align: center;"><img src="https://geekingwithmauri.com/assets/posts/15_swiftUITesting/testPlan/2.jpg" alt="test plan setup" /></p>

<ul>
  <li>Select the desired location</li>
</ul>

<p style="text-align: center;"><img src="https://geekingwithmauri.com/assets/posts/15_swiftUITesting/testPlan/3.jpg" alt="test plan location" /></p>

<ul>
  <li>After that, we can close the scheme window and the test plan setup window should be open. In it we’re going to setup our desired context</li>
</ul>

<table>
  <tbody>
    <tr>
      <td><img src="https://geekingwithmauri.com/assets/posts/15_swiftUITesting/testPlan/4.1.jpg" alt="Test plan in English" /></td>
      <td><img src="https://geekingwithmauri.com/assets/posts/15_swiftUITesting/testPlan/4.2.jpg" alt="Test plan in Spanish" /></td>
    </tr>
  </tbody>
</table>

<ul>
  <li>Now all that’s left missing is adding our snapshot tests for Spanish. We’ll also tweak them so they don’t get executed when the device is English.</li>
</ul>

<figure class="highlight"><pre><code class="language-swift" data-lang="swift"><span class="kd">func</span> <span class="nf">test_initialRendering_forSpanishLocaleAtDarkMode</span><span class="p">()</span> <span class="k">throws</span> <span class="p">{</span>
    <span class="c1">// This test will be skipped if current device's languege is English</span>
    <span class="k">try</span> <span class="kt">XCTSkipIf</span><span class="p">(</span><span class="kt">Locale</span><span class="o">.</span><span class="n">current</span><span class="o">.</span><span class="n">languageCode</span> <span class="o">==</span> <span class="s">"en"</span><span class="p">)</span> 
    <span class="nf">verifyRendering</span><span class="p">(</span><span class="nv">for</span><span class="p">:</span> <span class="o">.</span><span class="n">iPhone8</span><span class="p">,</span> <span class="nv">testName</span><span class="p">:</span>  <span class="kd">#function</span><span class="p">)</span>
    <span class="nf">verifyRendering</span><span class="p">(</span><span class="nv">for</span><span class="p">:</span> <span class="o">.</span><span class="n">iPhone8Plus</span><span class="p">,</span> <span class="nv">testName</span><span class="p">:</span> <span class="kd">#function</span><span class="p">)</span>
    <span class="nf">verifyRendering</span><span class="p">(</span><span class="nv">for</span><span class="p">:</span> <span class="o">.</span><span class="n">iPhone12ProMax</span><span class="p">,</span> <span class="nv">testName</span><span class="p">:</span> <span class="kd">#function</span><span class="p">)</span>
    <span class="nf">verifyRendering</span><span class="p">(</span><span class="nv">for</span><span class="p">:</span> <span class="o">.</span><span class="nf">iPhone8</span><span class="p">(</span><span class="o">.</span><span class="n">landscape</span><span class="p">),</span> <span class="nv">testName</span><span class="p">:</span> <span class="kd">#function</span><span class="p">)</span>
    <span class="nf">verifyRendering</span><span class="p">(</span><span class="nv">for</span><span class="p">:</span> <span class="o">.</span><span class="nf">iPhone8Plus</span><span class="p">(</span><span class="o">.</span><span class="n">landscape</span><span class="p">),</span> <span class="nv">testName</span><span class="p">:</span> <span class="kd">#function</span><span class="p">)</span>
    <span class="nf">verifyRendering</span><span class="p">(</span><span class="nv">for</span><span class="p">:</span> <span class="o">.</span><span class="nf">iPhone12ProMax</span><span class="p">(</span><span class="o">.</span><span class="n">landscape</span><span class="p">),</span> <span class="nv">testName</span><span class="p">:</span> <span class="kd">#function</span><span class="p">)</span>
<span class="p">}</span></code></pre></figure>

<p>💡 Very important: use <code class="language-plaintext highlighter-rouge">XCTSkipIf</code> for the English tests so they get skipped when running Spanish environment.</p>

<p>After running the test suite again (⌘ + U), it’ll get execute twice in order to check both languages 👏🏽</p>

<p><img src="https://geekingwithmauri.com/assets/posts/15_swiftUITesting/testPlan/5.jpg" alt="test plan location" /></p>

<p>We’re left with all the anchor images permutations stored for later comparisons.</p>

<p><img src="https://geekingwithmauri.com/assets/posts/15_swiftUITesting/testPlan/6.jpg" alt="test plan location" /></p>

<h2 id="final-thoughts">Final thoughts</h2>
<p>There’s a place in the testing pyramid for manual verification but it’s reserved for hard to replicate edge cases. For more mundane layout and general UI validations, Snapshots are usually good enough.</p>

<p>Nevertheless it’s worth mentioning the toll they incur in performance. Unit tests are blazing fast (usually under 1 ms each one). Snapshots are on average 100 times slower, therefore we should be careful not to relay so heavily on them.</p>

<p>As always, there’s no silver bullets regarding software development. There will be times when it makes sense to pay for the performance penalty in order to cover a legacy screen, just to mention a common scenario. Each context is different so let’s not paint ourselves into a corner only for following “best practices”. Happy testing 👨🏽‍💻👋🏼</p>]]></content><author><name>Mauricio Chirino</name></author><category term="swift" /><category term="SwiftUI" /><category term="testing" /><category term="unit tests" /><category term="macOS" /><category term="localization" /><summary type="html"><![CDATA[The new shiny framework from Apple is not very intuitive on the testing front, or is it? How can we make it so?]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://geekingwithmauri.com/assets/resources/logo.png" /><media:content medium="image" url="https://geekingwithmauri.com/assets/resources/logo.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Unit tests are not silver bullets</title><link href="https://geekingwithmauri.com/work/testing.html" rel="alternate" type="text/html" title="Unit tests are not silver bullets" /><published>2021-09-29T00:00:00+00:00</published><updated>2021-09-29T00:00:00+00:00</updated><id>https://geekingwithmauri.com/work/testing</id><content type="html" xml:base="https://geekingwithmauri.com/work/testing.html"><![CDATA[<p style="text-align: center;"><img src="/assets/posts/14_unitTesting/cover.jpg" alt="reflexive cat" />
Photo from <a href="https://codeconstruct.wordpress.com/2019/02/07/enhancing-your-unit-tests-with-pressius/">Code Construct</a></p>

<blockquote>
  <p>Your test suite can’t fail if you don’t have any tests</p>

  <p>vs</p>

  <p>100% test coverage is REQUIRED</p>
</blockquote>

<p>Let’s address the elephant in the room by saying neither one of them is true.</p>

<h2 id="no-tests-no-problems-️">No tests, no problems 🤦🏽‍♂️</h2>

<p>This has been pretty much the state of the art for the last number of years in all the software factories (at least the ones <a href="https://www.linkedin.com/in/mauriciochirino/">I’ve worked on</a>) and I can tell for experience it’s no fun at all. You end up shipping code with close to zero confidence whenever there’s no a full <strong>manual</strong> regression test. Fair to say this is rarely the case so stress is constantly ramping up.</p>

<p>The scenario described above makes the entire CI/CD process unattainable due to software’s exponential complexity. The more code we write, the more states’ permutations we have to keep track of. This is simply not an scalable endeavor by any measure without involving some form of automation.</p>

<h2 id="every-single-line-of-code-has-to-be-covered-">Every single line of code HAS TO be covered 😰</h2>

<p>Then there’s the other end of the spectrum: <em>100% coverage dreamland</em>. This goal might be something achievable whenever we’re starting a project from scratch with the mindset it will reach production state no matter what AND will have a lifespan of months or longer. Think real hard for a moment about those two conditions.</p>

<p>Most software projects start either as a side hustle/hobby or an MVP to validate market-fit as soon as possible. Suffice to say neither one of them have unit tests in its initial scope, nor they should if you ask me. At such early stage, there’s no point to heavily invest time in designing a robust architecture -let alone setting a test suite with unit, integration, E2E tests and so forth- without any certainty that the project will live long enough to reap the benefits of said investment.</p>

<p><img src="/assets/posts/14_unitTesting/cost.jpg" alt="cost graph" /></p>
<p style="text-align: center;">Source: <a href="https://youtu.be/TQ9rng6YFeY">great talk</a> about the economics of software design by J.B. Rainsberger</p>

<p>Then, there is the real world. The harsh reality the vast majority of us face is that we inherit legacy code bases every time we enter a new job. Adding test coverage to an already productive software project is a noble but often futile effort since there’s no real value to the business in it by itself.</p>

<h2 id="werent-you-in-favor-of-testing-mauri-wtf-">Weren’t you in favor of testing Mauri, WTF? 🤨</h2>

<blockquote>
  <p>When a measure becomes a target, it ceases to be a good measure. – Goodhart’s law</p>
</blockquote>

<p>If you’ve been reading my blog for awhile now, all of this might sound as a hard left turn coming from me. I get it, but the reason I’m hammering on 100% test coverage absolute is because there’s no real point in doing so.</p>

<p>As stated above: <em>more code = more complexity</em> so % of test coverage by itself is no guarantee of proper operation. <strong>Wrong incentives</strong> are often the source of many evils. For instance a more Jr developer might be tempted to write tests that add no real value nor cover any actual use cases only to keep coverage “high”.</p>

<p>An old college used to call this practice “testing setters and getters”. That is just going through code via the test suite without actually asserting anything important (a business rule, validation policy or state change). This is a poor use of everyone’s time and it might be much better spent <a href="/swift/documentation-in-practice.html">documenting</a> or learning a <a href="/swift/serverSideVapor.html">new framework</a> that will make us more productive.</p>

<h2 id="what-are-test-useful-for-then-">What are test useful for then? 🧐</h2>

<h4 id="unit-tests-are-a-great-tool-to-automate-and-validate-atomic-behaviors-in-our-objects-">Unit tests are a great tool to automate and validate atomic behaviors in our objects 🤖</h4>

<p>Let us picture the time-consuming and boring task of launching an app, doing all the ceremony involved in reaching a desired context (depending of the feature/bug we’re dealing with, this might include login/registering and a bunch of non-related steps to what we actually want to check) and finally getting to the view we’re working in to verify for ourselves the changes we made.</p>

<p>Sounds like a lot for any simple change, right? And remember, as the system evolves, so does complexity. All that boilerplate won’t go away, on the contrary: we’ll need to add previous steps whenever a new feature is included and A/B experiments are running.</p>

<p>Unit tests get ride of all that boilerplate by simply invoking the desired object –SUT– with its required dependencies, execute the desired change we want to test and finally asserting the outcome is the expected one. All of this is now automated.</p>

<h4 id="they-aid-in-the-clean-design-of-systems-">They aid in the clean design of systems 🧼</h4>

<p>A popular strategy for writing test is following the <em>given, when, verify</em> approach. Fair enough, when we follow this simple recipe and start noticing that any part is getting larger than it should then we might have caught a code smell.</p>

<ul>
  <li>We might be in presence of a <em>god object</em>, requiring too many dependencies to be initialized on the <em>given</em> part</li>
  <li>Our logic might be scattered around multiple places when there has to occur multiple steps at the <em>when</em> part in order to get the object to the desired state</li>
  <li>There are way too many asserts in a <em>verify</em> part which could reveal unrelated things being affected by a single state change.</li>
</ul>

<p>All of this is of course circumstantial and the rules aren’t set on stone but again, unit tests aid in revealing these sort of code smell rather sooner than later.</p>

<h4 id="they-enable-proper-and-constant-refactoring">They enable proper and constant refactoring</h4>

<p><em>Refactor</em> is a widely <a href="/work/refactoring.html">misused and unpopular term</a> but the truth of the matter is that without automated testing there’s simply no way of constantly performing it. One of the ideals behind well crafted software is, as it evolves, more tasks become automated. It shouldn’t need more human power to directly monitor each and every single step we make or feature we add.</p>

<p>Unit tests should, at the very minimum, validate the simplest, most boring use cases and context.</p>

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

<p>As most things in life, there’s rarely an absolute truth and testing isn’t the exception. We should aim to apply them whenever they make sense to do so. I want to leave you guys with a quick reflection below I remember all the time when dealing with <a href="/books/drivingTechChange.html">time-crunched folks</a>.</p>

<p>Uncle Bob tells, at one of his books (don’t remember if it was <a href="https://amzn.to/3m5kaJ9">Clean code</a> or <a href="https://amzn.to/2XYQc1v">The clean coder</a>), the history of the first doctor to realize that hand washing before surgery could help saving lives. His colleges at the time routinely discarded this recommendation, claiming “they didn’t have time to do so because were too busy”.</p>

<p>The reality is that principles should remain as constants as possible at all times. These automatics systems are the ones that will make our lives easier tomorrow. We’ll thank ourselves by doing the right thing early on.</p>]]></content><author><name>Mauricio Chirino</name></author><category term="work" /><category term="unit tests" /><category term="coding" /><category term="rant" /><category term="testing" /><summary type="html"><![CDATA[Are automated tests really that necessary? If so, why aren't we writing more of them? Is TDD a cult?]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://geekingwithmauri.com/assets/resources/logo.png" /><media:content medium="image" url="https://geekingwithmauri.com/assets/resources/logo.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">What refactoring is not</title><link href="https://geekingwithmauri.com/work/refactoring.html" rel="alternate" type="text/html" title="What refactoring is not" /><published>2021-09-12T00:00:00+00:00</published><updated>2021-09-12T00:00:00+00:00</updated><id>https://geekingwithmauri.com/work/refactoring</id><content type="html" xml:base="https://geekingwithmauri.com/work/refactoring.html"><![CDATA[<p style="text-align: center;"><img src="/assets/posts/13_refactoring/cover.jpg" alt="compass" />
Photo from <a href="https://qualitycoding.org/refactoring-demo/">Quality coding</a></p>

<blockquote>
  <p>Let’s get this done quickly and we’ll refactor it afterwards, when there’s <strong>more</strong> time - Someone who’s about to code irresponsibly and disguise it as “technical debt”</p>
</blockquote>

<p>Fewer things get under my skin more than butchering language. I’m a bit of dork in that aspect so I like calling things for their proper names. One term that has a bad rep among stakeholders, product managers, technical and non technical leaders is “refactoring”.</p>

<ul>
  <li>“Oh, I need some time to <em>refactor</em> x functionality to prepare for y feature”</li>
  <li>“I’ll be <em>refactoring</em> <strong>__</strong> (fill the blank) feature in the meantime, it won’t take me long”</li>
</ul>

<p>The phrases said above by themselves are not dangerous. The real danger lies in the practice (or lack thereof) they so often imply.</p>

<p>Let’s start by stating the concept from the man itself (Martin Fowler)</p>

<blockquote>
  <p><strong>Refactoring</strong> is a disciplined technique for restructuring an existing body of code, altering its internal structure without changing its external behavior. - <a href="https://refactoring.com">Martin Fowler</a></p>
</blockquote>

<p>He wrote <a href="https://amzn.to/38X3U6z">an entire book</a> devoted to this discipline (which I highly recommend), specially to coding colleagues who might be misusing the term without even notice it.</p>

<p>Let me explain where I’m going with this: slap some peaces of functionality together, make it barely work in the happy path and announce it’s completed under the promise of “refactoring it later” is just jargon for lazy coding (in my opinion). We can’t touch said code later, without documentation, test coverage or even the same mindset we had when we wrote it in the first place, and <strong>guarantee</strong> we won’t be introducing bugs. At least not without performing <em>manual testing</em>, which is almost never the case.</p>

<p>What’s the point in coding, which ideally should automate more manual labor as it grows, if at the end of the day we’re constrained by an ever increasing number of boring manual tasks associated with it?</p>

<h2 id="weve-been-using-the-wrong-metaphors-all-along">We’ve been using the wrong metaphors all along</h2>

<p>In the world of programming, we often hear a lot of terms associated with architecture and buildings. But let’s stop for a second a think what would happened if a civil engineer would tell to his crew:</p>

<p>“Well folks, we need to get this done for this weekend because of the deadline. Cut corners where you need to, we’ll figure it out later”</p>

<p>No too many building would still be standing, right? I like to think more about software like a garden and we are its caretaker. A garden needs constant maintenance and it evolves with time. A full grown tree doesn’t require nearly the same level of nurturing as it did when it was just a small seed growing its roots on the ground. This is the lens through we should be looking at a system and that’s only possible with <strong>disciplined</strong> refactoring.</p>

<p>What exactly does disciplined refactoring imply? Glad you ask: it means adding tests to cover our <del>asses</del> backs <strong>before</strong> making any changes to the current code. This is the best way to guarantee we’re not introducing any bugs due to the last changes.</p>

<p>It’s not a perfect strategy. Our assumptions could be wrong which in turn would mean will end up validating wrong behavior (or maybe validating an scenario it will never happen). The thing with testing is that it forces us to <a href="/work/thinkingBeforeCoding.html">think before actually</a> coding.</p>

<p>On the flip side, the alternative -moving stuff around hoping nothing breaks- is just finger crossing 🤞🏽.</p>

<h2 id="you-cant-make-an-omelette-without-breaking-some-eggs">“You can’t make an omelette without breaking some eggs”</h2>

<p>“I inherited a legacy codebase Mauri, what you’re proposing doesn’t apply in those cases” I hear you saying. There’s another <a href="https://amzn.to/3twP8x3">excellent book</a> dedicated to address said scenario. I’m not pulling this out my butt people, way more experienced and truly prolific programmers than me have gone through these roads and they all pretty much converge in the same key points</p>

<ul>
  <li>Identify pain points</li>
  <li>Break dependencies</li>
  <li>Cover the desired piece of code that’s <strong>about to change</strong> with tests</li>
  <li>Make your changes</li>
  <li><em>Refactor</em> all you want afterwards.</li>
</ul>

<h2 id="when-the-remedy-is-worst-than-the-disease">When the remedy is worst than the disease</h2>

<p>Sometimes, just sometimes, the piece of legacy code we’re trying to alter is in such a bad state that they most sensible thing to do is starting from scratch. True to be told, these are rare scenarios. What’s more realistic is that the engineer who’s assigned this endeavor simply doesn’t want take the time of following the recipe stated above (either by lack of knowledge, interest or both) and prefers the clean slate approach.</p>

<p>But for argument sake, let’s just pretend that we are dealing with one of these rare scenarios. Ok then, we’re simply not talking about refactoring here. This is a rewrite and that’s definitively something to escalate due to the cheer amount of work it’ll require. Otherwise it’ll all end up increasing the bad rep refactoring currently enjoy.</p>

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

<p>Let’s call things for what they are and many hard conversations will flow naturally.</p>

<ul>
  <li>Did you make a mistake estimating a feature and need to cut corners to make it? Don’t call it technical debt unless you’re at the very minimum leaving breadcrumbs behind you in the form of documentation and some form of test coverage.</li>
  <li>Are you having a bad time understanding some piece of legacy code and think it’ll be faster starting from scratch? Think again: that piece of code you’re so eager to get rid of is already <em>productive</em> and <em>validated</em> in more shapes and forms than any unmaterialized ideas you have in your head.</li>
  <li>Feeling the “need” of refactor the whole project to make it “more scalable”? BAD IDEA. <strong>Refactor only what needs to change</strong>, the rest of it leave it alone. Part of this job is learning to keep our ego in check and accepting good enough productive beats “perfect” unfinished code every single time.</li>
  <li>There’s a “hard” deadline and you’re getting pressure to deliver something you know it’ll be unpredictable? This is a hard line to walk but part of being professional is having these difficult conversations with managers and/or stakeholder. No manager stakeholder in their right mind will compromise system stability over a new feature, if so then there’s a bigger problem within the team you belong to.</li>
</ul>

<p>We’re trust to deliver because we’re the experts after all. This means we’re getting paid not only to write code but also to provide our two cents on any topic where we’re part of. The sooner we all realized and understand this great responsibility, the sooner an equally great power will be unlocked alongside it, allowing us to make better decisions not only for us but for everyone involved.</p>]]></content><author><name>Mauricio Chirino</name></author><category term="work" /><category term="engineering" /><category term="coding" /><category term="rant" /><category term="success" /><category term="unit tests" /><summary type="html"><![CDATA[Fewer terms have been misused more times than refactoring. Let's see when and where it really applies.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://geekingwithmauri.com/assets/resources/logo.png" /><media:content medium="image" url="https://geekingwithmauri.com/assets/resources/logo.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Books I’ve read: Driving Technical Change (by Terrence Ryan)</title><link href="https://geekingwithmauri.com/books/drivingTechChange.html" rel="alternate" type="text/html" title="Books I’ve read: Driving Technical Change (by Terrence Ryan)" /><published>2021-07-28T00:00:00+00:00</published><updated>2021-07-28T00:00:00+00:00</updated><id>https://geekingwithmauri.com/books/drivingTechChange</id><content type="html" xml:base="https://geekingwithmauri.com/books/drivingTechChange.html"><![CDATA[<p style="text-align: center;"><img src="/assets/books/drivingTechnicalChange.jpg" alt="bookCover" /></p>

<p>You can’t accomplish great things completely on your own. That’s a fact I struggled way longer than I should due to the disproportionate ego I had (and till some degree still carry with me). We need people on our side to build solutions together. How can we reach out in order to harness all that human potential in the right direction?</p>

<p>The first thing to learn here is this: we’re emotional creatures. No matter how hard we fool ourselves into believing <strong>only</strong> rationale motivate us. In that regard, a combination of both soft skills and politics will talk you a long way in influencing your peers into adopting the practices and technologies you’re pushing forward. This is the premise of <a href="https://amzn.to/3ePK00T">Driving technical change: why do people on your team don’t act on good ideas and How to convince them they should</a>.</p>

<p>The author lays out a couple of interesting blueprints for us and how they relate to each other. Let’s check them out</p>

<ul>
  <li>
    <p><a href="#skeptic-patterns">Skeptic patterns</a>: here’s exposed, in an exaggerated matter acknowledge by the author himself, some of the most common folks we’ll likely find in our organizations. One important thing to note is that no pattern excludes another so we shouldn’t get surprised when <em>X</em> or <em>Y</em> teammate has more than a single trait in his or her personality.</p>

    <ul>
      <li><a href="#uninformed">Uninformed</a></li>
      <li><a href="#herd">Herd</a></li>
      <li><a href="#cynic">Cynic</a></li>
      <li><a href="#burned">Burned</a></li>
      <li><a href="#crunched">Time Crunched</a></li>
      <li><a href="#boss">Boss</a></li>
      <li><a href="#irrational">Irrational</a></li>
    </ul>
  </li>
  <li>
    <p><a href="#techniques-to-approach-them">Techniques to approach them</a>: once identified the predominant trait a fellow colleague of ours posses, it’s time to move on into action in order to persuade them. <em>Spoiler alert</em>: logic alone won’t do the trick here.</p>

    <ul>
      <li><a href="#gain-expertise">Gain expertise</a></li>
      <li><a href="#deliver-your-message">Deliver your message</a></li>
      <li><a href="#demostrate-your-technique">Demonstrate your technique</a></li>
      <li><a href="#propose-compromise">Propose compromise</a></li>
      <li><a href="#create-trust">Create trust</a></li>
      <li><a href="#get-publicity">Get publicity</a></li>
      <li><a href="#focus-on-sinergy">Focus on synergy</a></li>
      <li><a href="#build-a-bridge">Build a bridge</a></li>
      <li><a href="#create-something-compelling">Create something compelling</a></li>
    </ul>
  </li>
</ul>

<h2 id="skeptic-patterns">Skeptic patterns</h2>

<h3 id="uninformed">Uninformed</h3>
<p>This might be the easiest one to bring into your crew. The main reason they’re not using any given technology and/or technique is simply lack of awareness about it. Remember no everyone in tech keeps going beyond working hours, and that’s ok.</p>

<h3 id="herd">Herd</h3>
<p>They are the folks following the guidelines set on them, so there shouldn’t be any issue in informing and training them into joining your cause.</p>

<h3 id="cynic">Cynic</h3>
<p>This one is a special case in my humble opinion. The underlying motivation behind their attitude could be just looking smarter that someone else in front of an audience in order to impress and maintaining some form of status quo. Others just don’t want to put in the work involved in growing and therefore draw on into cynicism as a justification. Their Achilles heel: make your solution the smart choice, so questioning it makes them by comparison less smart. Their own egos will keep them in check this way.</p>

<h3 id="burned">Burned</h3>
<p>This group demands empathy from us since they share some “tragic” history with the technology/technique you’re trying to implement. Having TDD as a policy imposed on them when they weren’t still capable enough to produce solutions fast enough alongside tests could be an example of this. In said scenario, there wasn’t the chance to appreciate tests for the worth they bring to the table but instead look at them as bureaucracy to fulfill before doing “the actual work”.</p>

<h3 id="time-crunched">Time Crunched</h3>
<p><img src="/assets/posts/12_drivingTechChange/busy.jpg" alt="tooBusyToImprove" />
<a href="https://www.flickr.com/photos/toddle_email_newsletters/15596940251">source</a></p>

<p>The image above says it all about this group. Their motto is: “there’s never time to do it right, but there’s time to do it twice.” The thing to keep in mind here is that, even though their current method may waste time, they know exactly how much of it will be wasted. Some of them are truly time crunched because their workload exceeds their resources. The key with them is showing them your solution as a relief. Save them time and they’ll play along with us and whatever we’re selling.</p>

<h3 id="boss">Boss</h3>
<p>Our industry has these tricky power balances that don’t truly make sense anywhere else. For instance, your boss probably doesn’t understand the technology you’re using and it’s completely ok (even expected) since IT thrives with specialization. It’s how we communicate our ideas and insights that will make or break whatever lever we’re trying to pull. The key here is speaking in boss’ language: there’s little value to your non technical manager <em>how much more efficient a piece of algorithm is</em>; on the other hand if what you did <em>reduces ongoing project costs</em>… Now we all understand.</p>

<h3 id="irrational">Irrational</h3>
<p>The last one of the group is a dead end and how you deal with them is completely different of any of the previous approaches. For starters: you can’t convince them of anything. Instead your job is to contain them. Try to ignore their complains as long as possible and have management to mandate your technique on them. I know it sounds authoritarian but some people just want to see the world burn 🃏</p>

<h2 id="techniques-to-approach-them">Techniques to approach them</h2>

<h3 id="gain-expertise">Gain expertise</h3>
<p>You need to master whatever you’re selling to your organization. If it is implementing CI, testing practices and/or documentation, you need to become knowledgeable in these subjects in order to help others whenever and wherever they get stuck. The tough part will be finding someone to teach your learning afterwards. Be humble at all times and remember: you’re trying to convert people leading by example, not turning them into <a href="#burned">burned ones</a>.</p>

<h3 id="deliver-your-message">Deliver your message</h3>
<blockquote>
  <p>If you build it, they will come – Tech fallacy 101</p>
</blockquote>

<p>If we don’t tell people about our tool, they’ll never adopt it. However, there’s the right and the wrong way to do this.</p>

<ul>
  <li><em>Wrong way</em> ❌: “your choice is wrong. Pick mine instead.”</li>
  <li><em>Right way</em> 👏🏽: “let me show you how <em>X</em> can help us with <em>Y</em>.”</li>
</ul>

<p>There are no absolutes and most of the time one thing doesn’t automatically exclude another so unless the current state isn’t affecting what your promoting don’t even address it. Keep in mind that what you say is <em>less</em> important than <em>how</em> you say it.</p>

<h3 id="demonstrate-your-technique">Demonstrate your technique</h3>
<blockquote>
  <p>Setting an example is not the main means of influencing others, it is the only means. - Albert Einstein.</p>
</blockquote>

<p>It is one thing to talk about a theoretical improvement and it is quite another to show it in action. Besides, people believe what they are shown more than what they’re told. Whatever you’re pushing into compliance in your team, find a way to demo it. Extra points if you can time it properly, i.e. solving a pain point with said solution.</p>

<h3 id="propose-compromise">Propose compromise</h3>
<p>This technique propose the bold move to challenge the status quo. In all organization there are rules that were set in response to an incidents; policies to prevent those incidents from occurring again. The thing is that most of the time such policies fall outdated and nobody ever questions them again. Developers should be able to say “We must do <em>X</em> because of <em>Y</em>”, if no one remembers this <em>Y</em> then you’re in front of a questionable policy.</p>

<p>Just keep in mind that no every organizational rule can be perfectly replaceable with a technology change. Manual testing is a good example of this: no matter how tedious it might be, no amount of automation and test coverage will completely remove it from the picture. The best we all can do in this instance is to automatize the boring cases as much as possible.</p>

<h3 id="create-trust">Create trust</h3>
<p>If people don’t trust us, we’ll be fighting uphill even justifying the most obvious tools. The cautionary tell with this technique is warning us about inducing fear to manipulate our coworkers into doing something just out of fear. Eventually people wise up, the word is out and not only does this renders the entire technique useless but our reputation suffers a big blow.</p>

<p>In an environment with big egos and a whole bunch of buzzwords flying around all day, being wrong and openly admitting it allows people to trust you. Don’t be the guy who knows something is wrong but is too committed into it to backpedal just out of fear of owning a mistake.</p>

<h3 id="get-publicity">Get publicity</h3>
<p>You need to build some amount of street cred in order to demonstrate reliability in whatever you’re proposing. You think testing is a necessary practice? You better have at least a <a href="/swift/testing3rdFwk.html">post in your blog</a> relating how to do it properly.</p>

<h3 id="focus-on-synergy">Focus on synergy</h3>
<p>Unless you’re employed by a technical firm, technology doesn’t set the direction for your company. Business needs will always trump technology concerns so your tool must be aligned said needs, this is how you create a stronger argument around it.</p>

<p>Can your technique make scaling easier? Reduce spending? Increase conversion rates? Then management will be behind you because they will feel listened instead of constrained by engineering.</p>

<h3 id="build-a-bridge">Build a bridge</h3>
<p>Bridges are half baked tools or techniques. They aren’t the ultimate solution but they differ from the current status quo. Think of them as a step in the right direction toward the goal. Remember: multiple little changes are often easier to achieve than a massive big one.</p>

<p>An example of this is <a href="https://www.agilealliance.org/glossary/tdd/#q=~(infinite~false~filters~(postType~(~'page~'post~'aa_book~'aa_event_session~'aa_experience_report~'aa_glossary~'aa_research_paper~'aa_video)~tags~(~'tdd))~searchTerm~'~sort~false~sortDirection~'asc~page1)">TDD</a>. This isn’t something a developer achieve in a week of training, it’s a discipline developed over years and years of practice and consistency. The other side of the spectrum is no coverage at all so a bridge in this scenario might be starting out with some unit tests. When the team is consistently writing them, you could push forward integration test and maybe some snapshot tests as well and so forth.</p>

<h3 id="create-something-compelling">Create something compelling</h3>
<p>People are much more accepting of something they’ve resisted when they are the ones to choose to stop resisting, rather than having someone else force them into using it. For instance: a reason people might resist writing documentation is because they don’t see the value in it since it’s hard to find afterwards. <a href="/swift/documentation-in-practice.html">Build a tool</a> that facilitate this and you’ll bring followers to your cause.</p>

<h2 id="so-how-do-all-this-come-together">So how do all this come together?</h2>
<p>I know, I know… I just went on and thrown a whole lot of information with no clear relation between on side and the other. The gist of it all is that each type of skeptical can be handled with a particular set of techniques.</p>

<p><img src="/assets/posts/12_drivingTechChange/relationship.jpg" alt="match" /></p>

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

<p>As stated at the beginning: we humans are <strong>emotional</strong> creatures son don’t overestimate the effectiveness of these new learned techniques in the art of influencing people.</p>

<p>Start small and grow your following as you go. Kick off your journey by ignoring the <em>irrational</em>, target those willing to improve themselves and harness the newly converted ones in order to sway management so your solutions finally become the norm.</p>

<p>Just remember: problems are infinite and you’ll have to start over each time there’s something you want to change. This is a journey, not a destination so above all be patient. I encourage you to go read the whole book and not just take my word for it, some of the examples will show you the point far better than this summary.</p>]]></content><author><name>Mauricio Chirino</name></author><category term="books" /><category term="engineering" /><category term="developer" /><category term="software" /><category term="coding" /><category term="people" /><summary type="html"><![CDATA[How then do you properly influence your peers into adopting practices and technologies you're pushing foward?]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://geekingwithmauri.com/assets/resources/logo.png" /><media:content medium="image" url="https://geekingwithmauri.com/assets/resources/logo.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Skills for succeeding on software development</title><link href="https://geekingwithmauri.com/work/succeedingAtDevelopment.html" rel="alternate" type="text/html" title="Skills for succeeding on software development" /><published>2021-05-26T00:00:00+00:00</published><updated>2021-05-26T00:00:00+00:00</updated><id>https://geekingwithmauri.com/work/succeedingAtDevelopment</id><content type="html" xml:base="https://geekingwithmauri.com/work/succeedingAtDevelopment.html"><![CDATA[<p style="text-align: center;"><img src="/assets/posts/11_technicallSuccess/cover.jpg" alt="compass" />
Photo by <a href="https://www.pexels.com/@ekaterina-bolovtsova?utm_content=attributionCopyText&amp;utm_medium=referral&amp;utm_source=pexels">Ekaterina Bolovtsova</a> from <a href="https://www.pexels.com/photo/photo-of-person-using-laptop-4048765/?utm_content=attributionCopyText&amp;utm_medium=referral&amp;utm_source=pexels">Pexels</a></p>

<p>There’s no need to <a href="https://www.reddit.com/r/funny/comments/9oyn0p/best_of_luck_reaching_30_john/">age prematurely while coding</a>. As all cognitive intensive jobs, programming is hard and it is true there’s some degree of complexity no amount of good practices and new technologies will solve. That’s why we’re getting payed after all, isn’t it? To manage complexity. However there are a few ground rules that will take you a long way despite how complex your work environment might be.</p>

<p>The way I’ve seen it from the inside –it’s been almost a decade since I’m doing this professionally (a decade and a half if we’d include college years)– There are two sides to this coin:</p>

<ul>
  <li>
    <p><a href="#technical-skills">Technical skills</a>: often called <em>hard skills</em>, they refer to the skill set you bring to the table as an engineer. Notice I’m referring to a whole set of abilities because coding alone will not even get you through the door. The following are a set of abilities you ought to master in your way to software development proficiency. I’m not a master myself, however I’d have the fortune to work alongside some prolific developers. All of them share these traits and we should all put effort into diving deep in these topics:</p>

    <ul>
      <li><a href="#handle-yourself-at-git">Handle yourself at Git</a></li>
      <li><a href="#write-good-documentation">Write good documentation</a></li>
      <li><a href="#no-laziness-for-testing">No laziness for testing</a></li>
      <li><a href="#keen-eye-for-design">Keen eye for design</a></li>
    </ul>
  </li>
  <li>
    <p><a href="#soft-skills">Soft skills (<em>people skills</em>)</a>: so many great coders make the mistake to think of these as an afterthought and oh boy they’re wrong to do so. No matter how smart you <em>think</em> are, nobody will want to work with you if: A) You’re an asshole B) They can’t understand you <strong>clearly</strong> C) <strong>Both</strong>. You work with <em>people</em>, develop solutions for <em>people</em> and get feature requests from <em>people</em>… Notice a common factor in those statements? (spoiler alert, it’s right in the subtitle of this part). It’s mandatory to develop social skills if you want to succeed long term, such as:</p>

    <ul>
      <li><a href="#proper-communication">Proper communication</a></li>
      <li><a href="#handle-stress">Handle stress well</a></li>
      <li><a href="#mentoring">Mentoring</a></li>
      <li><a href="#empathy">Empathy</a></li>
    </ul>
  </li>
</ul>

<h2 id="technical-skills">Technical skills</h2>

<h3 id="handle-yourself-at-git">Handle yourself at Git</h3>
<p>I used to work in a project that implemented <a href="https://git-scm.com/book/en/v2/Git-Tools-Submodules">Submodules</a> in a team where only <a href="https://www.linkedin.com/in/nicolas-alvarez-555243116/">one of the devs</a> knew how to properly manage himself around it (suffice to say the rest of us were pretty Jrs in this whole subject back then). Whatever the dependency manager involve in our codebases, we should strive toward a proficient domain in the system version control in our organization –most likely than not <em>Git</em>–.</p>

<p>As software engineer, our knowledge in Git shouldn’t limit to only committing, pushing and pulling. We should know how to properly solve merge conflicts, finding “lost” commits using <code class="language-plaintext highlighter-rouge">reflog</code>, versioning releases with tags so they are properly organized and easily findable remotely at all times. Git allows you all of this and more.</p>

<p>We should be able to perform all the above (among other things) comfortably from our terminal of choice. Don’t get me wrong, there’s no shame in using the aid of a GUI client for Git, but that doesn’t excuse us from getting to know and understand the ins and outs of our work flow.</p>

<p>In fact, it’s virtually free <a href="https://www.amazon.com/Pro-Git-Scott-Chacon-ebook/dp/B01ISNIKES">becoming a Pro</a> in Git. The only investment you need to make is your time and the R.O.I. will translate in less stress, complexity and overall predictability of your everyday working flow.</p>

<h3 id="write-good-documentation">Write good documentation</h3>
<p>As stated in detail in <a href="/swift/documentation-in-practice.html">one of my previous post</a>, writing <strong>good</strong> documentation is easier than ever. This is key for the scalability of any project since it eases the onboarding process of any newcomer, no matter his or her seniority level. Also, having a well documented system makes hard to introduce unnecessary complexity into it along the way.</p>

<p>Even though I specialized myself on Swift and other Apple related technologies, I know for a fact it’s painless generating good documentation for other platforms. Just to give an example involving Backend folks, there’s <a href="https://swagger.io/tools/swagger-ui/">Swagger UI</a> which allows us to visualize in a human friendly way the data flow in a given set of APIs as well as interact with it.</p>

<p><a href="https://swagger.io/tools/swagger-ui/"><img src="/assets/posts/11_technicallSuccess/swaggerExample.jpg" alt="SwaggerExample" /></a></p>

<h3 id="no-laziness-for-testing">No laziness for testing</h3>
<blockquote>
  <p>I know what I’m doing, I don’t need to write tests</p>

  <p>Tests slow me down</p>

  <p>It’s just a proof of concept, there’s no need to write tests.</p>
</blockquote>

<p>I’ve personally heard all of the above plus others. This tends to be a “polemic” topic, I even wrote <a href="/work/thinkingBeforeCoding.html">about it</a> before. It’s only polemic because it hurt some egos, but let’s say it again in case there’s a doubt: Tests don’t slow down software delivery any more than an shitty all-nighter crush session of coding will mess up your code base. You’ll realize this when trying to modify a little thing and break something else completely unrelated elsewhere. Those are the kind of headaches tests prevent.</p>

<p>I’d argue those “extra” <strong>minutes</strong> of pause between writing a failing test first and then implement the proper code to make it pass are <em>nothing</em> compare to <strong>the hours</strong> wasted in agony debugging logs (or adding them if they don’t exist) and spreading breakpoints all over the code base hunting bugs because “we knew what we were doing and therefore no tests were needed”. Give me a break 🤦🏽‍♂️.</p>

<h3 id="keen-eye-for-design">Keen eye for design</h3>
<p>This is kind of a segue to the next part but it’s still belongs to the technical side in my point of view. By having an eye for design I don’t mean necessarily developing skills with common design tools such as Adobe Photoshop, Figma, Sketch, inVision or the likes (although it wouldn’t hurt to say the truth). What I’m talking about is developing critical thinking regarding the designs you’ll be requested to implement in your UIs.</p>

<p>If you’re lucky enough to work alongside a UI team, there should be a healthy communication among all of you. We as developers shouldn’t be roadblocks in their infinite creativity and thrive to implement new things anymore than they should cause us headaches with impossible requests. This is only achieved by communicating effectively.</p>

<p>Your designer doesn’t need to know (nor cares) about the technical limitations your code base -or you for that matter- may have. All <a href="https://www.linkedin.com/in/nicolaszubia/">he</a> or <a href="https://www.linkedin.com/in/megermino/">she</a> cares is to materialize whatever they sketch into reality and you’ll be key in this part should you choose to be proactive about it.</p>

<p>Offer resistance whenever someone hands you a design that might go against <a href="https://developer.apple.com/design/human-interface-guidelines/">usability</a> and guide them with your system knowledge so both of you can arrive to the desired outcome. Collaborate with them and help them see what’s possible for any given deliverable in order to gain user insights from any new experiment your product is testing.</p>

<h2 id="soft-skills">Soft skills</h2>

<h3 id="proper-communication">Proper communication</h3>
<p>It’s not the entire responsibility of your interlocutor to completely understand what you mean 100% of the time. Put your ego aside whenever your first instinct is “This f3ck!ng idiot! Am I speaking in Latin? What part of what I just said wasn’t clear?! 🤬”.</p>

<p>Forget unnecessary jargon whenever possible, speak as if you were always talking to a non technical person and go into details only when asked to. This alone will get you the trust of those less experienced than you and non developers college as they will see you as someone approachable.</p>

<p>Also, now that most of our communication is written instead of verbal you need to be very careful with jokes, pun, or anything that might very well be misunderstood. In person chatting has all sort of body language and voice intonation coming into play that are simply gone when writing. Be concise and seize the advantage writing provides which is taking time to think before hitting <em>send</em>.</p>

<p>I know it’s a lot to digest but trust me, it’s better to be extra careful than having HHRR knocking on your door due to an offense you accidentally made by trying (and failing) to be funny.</p>

<h3 id="handle-stress-well">Handle stress well</h3>
<p>No matter the systems in place and the discipline you develop, there will be stressful times you’ll have to deal with. I’m not trying to be dramatic here but most often than no they will define you within your organization. It’s up to you if that definition is “a reliable colleague” or “the kid (no matter the age who breaks down under pressure”.</p>

<p>Meditate, make sure to sleep and eat well consistently and get some regular exercise. Let’s break the stereotype of the ungroomed guy with poor body complexion who’s sitting all night in front of the keyboard. You <strong>need</strong> to take care of your body as a whole in order to perform on the top level.</p>

<p>If it’s true your code should be predictable, it’s also true –until some degree– you should to and this can be greatly improved by being an organized professional. Aim to be as organized as possible with your professional stuff as well as your personal ones in order to keep unpleasant surprises out of your routine.</p>

<h3 id="mentoring">Mentoring</h3>
<blockquote>
  <p>“When One teaches, two learn” – <a href="https://amzn.to/3hRAf4r">Robert Heinlein</a></p>
</blockquote>

<p>As mentioned before, <a href="/swift/documentation-in-practice.html">you can’t accomplish great, sustainable things all alone</a>. Therefore it’s important to share your knowledge with others, there’s nothing but advantages to doing this such as cementing core in your understanding of complex topics, leveling up your team around you and enhancing the communication skills mentioned earlier. There’s no bigger prove of <a href="#proper-communication">proper communication</a> than teaching others.</p>

<p>A byproduct of mentoring others is than you slowly but surely create a following around you and that turns into <a href="https://amzn.to/3fPYEVB">influence</a> and leverage along the way.</p>

<h3 id="empathy">Empathy</h3>
<blockquote>
  <p>You have two ears and one month. Use them accordingly - <a href="https://amzn.to/3fmCpHR">Stephen Covey</a></p>
</blockquote>

<p>Remembers I mentioned above you work with people? Well all of us have good and bad days and these can even stretch out for long periods of time due to a variety of personal factors. Whenever possible pay attention to your surroundings, coworkers may need help and might be too shy to ask for it or don’t even know to do it. This is commonly disguised as poor performance but can be very well go to the other extreme: someone in your team overworking. Reach out to them.</p>

<p>Sometimes all it takes is listening someone to help them. I cannot stress enough how being an empathic colleague will help you and others around you fostering a healthy environment.</p>

<h2 id="conclusion">Conclusion</h2>
<p>I’m a big fan of <em>critical thinking</em>. Challenging the status quo whenever it made sense to do so has brought me some headaches but it also opened doors where I never thought possible. Remember: you’re not a monkey just hitting the keyboard, <strong>software engineers are paid to think</strong>.</p>

<p>People are often open to debate with the goal of achieving a better outcome for the final solution (if they aren’t you might be in a toxic environment so be careful). At the end of the day, you’re also a user so your opinion matter as well.</p>]]></content><author><name>Mauricio Chirino</name></author><category term="work" /><category term="coding" /><category term="no stress" /><category term="success" /><summary type="html"><![CDATA[What if I tell you there's no need to curse on hourly basis during every single working day while writing code? Sounds impossible? Not at all 🤓👨🏽‍💻]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://geekingwithmauri.com/assets/resources/logo.png" /><media:content medium="image" url="https://geekingwithmauri.com/assets/resources/logo.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Documentation in practice for Swift</title><link href="https://geekingwithmauri.com/swift/documentation-in-practice.html" rel="alternate" type="text/html" title="Documentation in practice for Swift" /><published>2021-05-06T00:00:00+00:00</published><updated>2021-05-06T00:00:00+00:00</updated><id>https://geekingwithmauri.com/swift/documentation-in-practice</id><content type="html" xml:base="https://geekingwithmauri.com/swift/documentation-in-practice.html"><![CDATA[<!-- ------------ -->

<p style="text-align: center;"><img src="/assets/posts/10_documentation/cover.jpg" alt="compass" />
Image by <a href="https://pixabay.com/users/myrfa-3126475/?utm_source=link-attribution&amp;utm_medium=referral&amp;utm_campaign=image&amp;utm_content=1614223">Ag Ku</a> from <a href="https://pixabay.com/?utm_source=link-attribution&amp;utm_medium=referral&amp;utm_campaign=image&amp;utm_content=1614223">Pixabay</a></p>

<blockquote>
  <p>“Without Knowledge, action is useless and knowledge without action is futile.” ― Abu Bakr</p>
</blockquote>

<p>If you ended up in this post, chances are you know you should document your code and currently aren’t doing it. Well, let me tell you it’s not enough to document it, this information <strong>MUST</strong> be easily accesible by anyone who wants to use it. But on the bright side, the fact that you’re here instead of procrastinating in Twitter shows progress on your behalf. Good for you 👏🏽</p>

<p>On the other hand, if you’re still uncertain about documenting regularly (you might even think you write code so well it documents itself), then please bare with me while I make my case. This post will be broken down into sections so feel free to skip ahead into a specific one, depending on your needs.</p>

<ul>
  <li><a href="#why-documenting-matters">Why documenting matters</a></li>
  <li><a href="#what-is-good-documentation">What is good documentation?</a></li>
  <li><a href="#how-to-do-it-effectively">How to do it effectively</a></li>
  <li><a href="#using-jazzy-for-the-job">Using jazzy for the job</a>
    <ul>
      <li><a href="#prerequisites">Prerequisites</a></li>
      <li><a href="#setup">Setup</a></li>
    </ul>
  </li>
  <li><a href="#use-case-in-practice">Use case in practice</a></li>
  <li><a href="#make-it-findable">Make it findable</a></li>
  <li><a href="#final-thoughts">Final thoughts</a></li>
</ul>

<p>With no further do, let’s dive in! 🤿</p>

<h2 id="why-documenting-matters">Why documenting matters</h2>

<blockquote>
  <p>The <strong>ratio of time</strong> spent reading versus writing is well over <strong>10 to 1</strong>. We are constantly <strong>reading old code</strong> as part of the effort to write new code. - <a href="https://amzn.to/2RyqCwn">Robert C. Martin, Clean Code</a></p>
</blockquote>

<p>If nothing else, do your future self a favor. As stated above, chances are you’re going to spend an awful lot of time reading through your code base; you might as well make it easier to read over time by adding documentation. Writing what a piece of code is supposed to do forces you to reason about it in an organized manner, hence I’d argue documenting also improves the <a href="/swift/testing3rdFwk.html">potential testability of it</a>.</p>

<p>Look at it this way: any serious piece of software you write should ideally be thought as a possible building block for something bigger tomorrow. There’s not too much point in creating an elegant, reusable solution if every time anyone (including you) wants to use it has to dig deep in source code to grasp its inner workings. This gap is easily filled with great documentation.</p>

<p>Think in all the code we write on daily basis, including all third party dependencies we use: from a <a href="https://developer.apple.com/documentation/uikit/uicollectionview">UICollectionView</a> to <a href="https://github.com/pointfreeco/swift-snapshot-testing">Snapshot testing</a>… How likely would we be to have them as our mental shortcuts for the domain they solve, let alone use them at all, if not for the great documentation there’s behind these kind of frameworks and libraries?</p>

<p>Finally, documenting is a great way to get involve in any open source project. And this in and of itself has <a href="https://opensource.guide/how-to-contribute/">many great benefits</a>.</p>

<h2 id="what-is-good-documentation">What is good documentation?</h2>

<p>In order to write effective documentation, the former must fulfill three principles:</p>

<ul>
  <li>*It must answer what a piece of code does, not necessary how (at least not in depth): If you’re going to use a <code class="language-plaintext highlighter-rouge">sorting</code> method you might not need to know (or even care for that matter) what type of algorithm is used for that task. Sure, we can mention it uses <a href="https://en.wikipedia.org/wiki/Quicksort">Quicksort</a> but going into details how it works is an overkill.</li>
  <li><em>Easy to find</em>: you can be the William Shakespeare of documentation but if nobody can read it when they need it, well then it’s as good as nothing. <strong>Good documentation</strong> must be <strong>findable outside your IDE</strong> of choice.</li>
  <li><em>Show, don’t tell</em>: this is one of those golden rules of writing. Don’t limit your documentation to a bunch of technical jargon. <strong>Show</strong> examples of usage and, when possible, where to use them.</li>
</ul>

<table>
  <tbody>
    <tr>
      <td><img src="https://geekingwithmauri.com/assets/posts/10_documentation/show.jpg" alt="show example" /></td>
      <td><img src="https://geekingwithmauri.com/assets/posts/10_documentation/tell.jpg" alt="don't tell example" /></td>
    </tr>
    <tr>
      <td>Show</td>
      <td>Don’t tell</td>
    </tr>
  </tbody>
</table>

<p>Document only what’s going to be used by the outside world. It’s a myth that every single line of code should be documented. There are better uses for your time so invest it where it counts (see the 3 points above when in doubt).</p>

<h2 id="how-to-do-it-effectively">How to do it effectively</h2>

<p>Writing documentation in Swift is great because it has markup support embedded so documentating is easier than ever nowadays. Xcode even provide us the appropriate template when we set the cursor on the name of our protocols/classes/structs and hit <kbd>⌘</kbd> + <kbd>⌥</kbd> + <kbd>/</kbd>. The example shown above was generating using this syntax:</p>

<figure class="highlight"><pre><code class="language-mkd" data-lang="mkd">/// Triggers a generic request in an asynchronous matter
///
/// Example of usage:
///
///      let endpointRequest = URLRequest(url: URL(validURL: "github.com"))
///      let sessionManager = RequestManager()
///      sessionManager.request(endpointRequest) { result in
///          switch(result) in
///          case success(let response):
///          // do whatever you wish with NetworkResult resulting type
///          case failure(let errorType):
///          // handle error appropriately
///      }
///
/// - Parameters:
///   - request: URL request specifications
///   - completion: Resulting completion closure from request</code></pre></figure>
<p>When <kbd>⌥</kbd> + click on top of any code that’s documented, rendering ocurrs and it’s shown in that pretty format. Since going into deep in markup formatting is beyone the scope of this post, I urge you to check for more details in the <a href="https://developer.apple.com/library/archive/documentation/Xcode/Reference/xcode_markup_formatting_ref/index.html#//apple_ref/doc/uid/TP40016497-CH2-SW1">official documentation</a> (no pun intended 🥁)</p>

<h2 id="using-jazzy-for-the-job">Using jazzy for the job</h2>

<p>We already established above that documentation must be findable beyond your IDE. In other words, anyone in need for any of your APIs shouldn’t have to go through the source code of them necessarily in order to consume them properly. So how do we achieve this? One way is using a great tool built specifically for this task: <a href="https://github.com/realm/jazzy">Jazzy</a>.</p>

<p>Jazzy is a command-line utility maintained by the folks of <a href="https://github.com/realm">realm</a> that generates documentation for Swift and Objective-C, directly from  both source code and compiled modules. It dumps all that generated docs into a well formatted static site that resembles Apple’s official documentation styling.</p>

<h3 id="prerequisites">Prerequisites</h3>

<p>The following is a list of optional (but highly recommendable) things you should have installed in your system before proceeding</p>

<ul>
  <li><a href="https://brew.sh">Brew</a>: this is a great package manager for macOS. It will ease many of the installations you’ll have to deal with in your environment setup, not only for jazzy and its requirements, but in general.</li>
</ul>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="nv">$ </span>/bin/bash <span class="nt">-c</span> <span class="s2">"</span><span class="si">$(</span>curl <span class="nt">-fsSL</span> https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh<span class="si">)</span><span class="s2">"</span></code></pre></figure>
<ul>
  <li><a href="https://github.com/rbenv/rbenv">rbenv</a> or <a href="https://rvm.io">RVM</a>: As a prerequisite for installing this gem (Jazzy), I recommend installing a <em>Ruby version manager</em> first. This is so you don’t run into common permission conflicts that otherwise arise when using OS X vanilla ruby version directly from the terminal (it usually starts failing due to lack of permissions and all sort of stuff).</li>
</ul>

<p>After doing some <a href="https://metova.com/choosing-a-ruby-version-management-tool-rbenv-vs-rvm/">digging</a> into which version manager to use, I decided to pick the first one essentially because I found it to be less invasive (RVM overrides shell commands and I have trust issues with that sort of politics).</p>

<p>Installing it via Brew is as simple as:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="nv">$ </span>brew <span class="nb">install </span>rbenv</code></pre></figure>
<p>We then setup <code class="language-plaintext highlighter-rouge">rbenv</code> in our terminal by simply typing:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="nv">$ </span>rbenv init</code></pre></figure>
<p>As a side note, the terminal your using will print a recommendation command for you whe it’s finished. Such command should be added into your bash profile in order to execute the step above automatically from now on. In my case I use <a href="https://ohmyz.sh">Oh My Zsh</a> so I added the line below at the bottom of my <code class="language-plaintext highlighter-rouge">.zsh</code> file.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>eval "$(rbenv init -)"
</code></pre></div></div>

<p>We check everything is working properly by running the <em>rbenv-doctor</em> script like so:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="nv">$ </span>curl <span class="nt">-fsSL</span> https://raw.githubusercontent.com/rbenv/rbenv-installer/main/bin/rbenv-doctor | bash</code></pre></figure>
<p>You should see everything working properly and telling you that there aren’t any ruby versions installed yet in rbenv path.</p>

<p>Go ahead and declare the desired ruby version to install in your project’s root path within a .ruby-version text file. At the time of this writing, <em>*the latest stable version</em> was <a href="https://www.ruby-lang.org/en/downloads/">3.0.1</a> so that’s the one we’ll install.</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell">rbenv <span class="nb">install </span>3.0.1</code></pre></figure>
<ul>
  <li><a href="https://bundler.io">Bundler</a>: I knew bundler through a colleague of <a href="https://www.linkedin.com/in/ariel-demarco-a4b34aa0/">mine at work</a>. All it does is centralizing all your Ruby gems and their respective versions for your project.</li>
</ul>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="nv">$ </span>gem <span class="nb">install </span>bundler</code></pre></figure>
<p>In case your terminal rejects the above command due to a lack of writing permissions, check your Gems environment by executing:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="nv">$ </span>gem <span class="nb">env </span>home</code></pre></figure>
<p>If above output is pointing to your system default Ruby version, it shouldn’t. That’s the entire reason we took all the trouble in setting up <strong>rbenv</strong> in the first place, but things sometimes go wrong. What <a href="https://github.com/rbenv/rbenv/issues/879#issuecomment-359284790">solved</a> this for me was rehashing rbenv like so:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="nv">$ </span>rbenv rehash</code></pre></figure>
<p>Now you should see some form of <strong>your user path</strong> from the output of the command below:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="nv">$ </span>gem <span class="nb">env </span>home
<span class="c"># =&gt; ~[your_username]/.rbenv/versions/&lt;ruby-version&gt;/lib/ruby/gems/...</span></code></pre></figure>
<p>We define our <a href="https://bundler.io/v2.2/gemfile.html#gemfiles">Gemfile</a> with our desired gems in it:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>source "https://rubygems.org"

gem "jazzy"
</code></pre></div></div>

<p>And install it via Bundler:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="nv">$ </span>bundler <span class="nb">install</span></code></pre></figure>
<h3 id="setup">Setup</h3>

<p>Setting up Jazzy is dirt simple:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="nv">$ </span>gem <span class="nb">install </span>jazzy</code></pre></figure>
<blockquote>
  <p>In case you want to avoid messing directly with <em>sudo</em> permissions in order to modify your system files –which <strong>I recommend against</strong> if you’re not <strong>COMPLETELY</strong> sure what you’re doing–, I’d suggest following the above steps detailed in the <a href="#prerequisites">prerequisites</a> section.</p>
</blockquote>

<p>The command <code class="language-plaintext highlighter-rouge">jazzy</code> should now be available in your terminal. All that’s left now is executing it with the proper set of flags configured for our case.</p>

<h2 id="use-case-in-practice">Use case in practice</h2>

<p>I followed the steps above in order to generate <a href="https://github.com/GeekingwithMauri/MauriNet/tree/main/docs">the documentation</a> you see for <a href="https://github.com/GeekingwithMauri/MauriNet">MauriNet</a>. This is a Networking wrapper I built using the Swift Package Manager (SPM) around Swift’s native URLSession so it wouldn’t be necessary to add third party dependencies for simple <code class="language-plaintext highlighter-rouge">GET</code>/<code class="language-plaintext highlighter-rouge">POST</code> requests and skipping the boilerplate ceremony each time I’d have to use it. Feel free to check it out. Feedback is not only welcome but encouraged, I’ll be delighted to receive pull requests in this and any of my other repos.</p>

<p>As a disclaimer, my library doesn’t have a <code class="language-plaintext highlighter-rouge">.xcworkspace</code> (it’s not a pod) nor a <code class="language-plaintext highlighter-rouge">.xcodeproj</code> commonly generated by Xcode for projects. That’s why I can simply execute in my terminal the following command without further arguments and have a successful response from it:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell">swift <span class="nb">test</span> <span class="nt">-v</span></code></pre></figure>
<p>For the other cases I stated above (cocoapods, custom projects with <code class="language-plaintext highlighter-rouge">.xcodeproj</code>) you must find the appropiate command that builds them via terminal in order to have jazzy generate docs for them.</p>

<p>The entire command executed for jazzy to generate the docs looks like this:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="nv">$ </span>jazzy <span class="se">\</span>
<span class="nt">--clean</span> <span class="se">\</span>
<span class="nt">--author</span> <span class="s2">"Mauricio Chirino"</span> <span class="se">\</span>
<span class="nt">--author_url</span> https://geekingwithmauri.com <span class="se">\</span>
<span class="nt">--github_url</span> https://github.com/GeekingwithMauri/MauriNet <span class="se">\</span>
<span class="nt">--output</span> docs <span class="se">\</span>
<span class="nt">--disable-search</span> <span class="se">\</span>
<span class="nt">--skip-undocumented</span></code></pre></figure>
<p>Since the line would have been too long to fit in a single snapshot, I specified new lines between each of the flags using <code class="language-plaintext highlighter-rouge">\</code> for the sake of explaining. Let’s see what each of them means:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">clean:</code> delete any previous output before generating a new one.</li>
  <li><code class="language-plaintext highlighter-rouge">author:</code> who’s behind this documentation.</li>
  <li><code class="language-plaintext highlighter-rouge">author_url:</code> where can this person (or company) be found.</li>
  <li><code class="language-plaintext highlighter-rouge">github_url:</code> project’s Github repo.</li>
  <li><code class="language-plaintext highlighter-rouge">output:</code> folder where the entire output will be dumped.</li>
  <li><code class="language-plaintext highlighter-rouge">disable-search:</code> this skips generating a search bar for the site. I found it confusing to use since the results were plain json instead of HTML page.</li>
  <li><code class="language-plaintext highlighter-rouge">skip-undocumented:</code> avoids generating doc for undocumented code. Really useful to hide work in progress within your modules/frameworks.</li>
</ul>

<h2 id="make-it-findable">Make it findable</h2>

<p>I know you probably thought after clicking on the <a href="https://github.com/GeekingwithMauri/MauriNet/tree/main/docs">the documentation generated above</a> “well hold on a secong Mauri, you said documentation must be easy to find. This is a not very friendly to read on!” and… You’d be partially right. Why do I say “partially”? If you download the project and head over the <code class="language-plaintext highlighter-rouge">/docs</code> folder, just by clicking on <em>index.html</em> you’ll be prompted into your browser and see an entirely functional documentation.</p>

<p>But since I’m kind of contradicting myself with all the extra steps above, let’s make this click-prof findable by setting <code class="language-plaintext highlighter-rouge">/docs</code> folder as a <a href="https://pages.github.com">Github page</a>:</p>

<ol>
  <li>After your documentation is pushed in your repository (ideally in its root), head over to its Github ⚙ Settings</li>
  <li>Select <strong>Pages</strong> on the left bar, near the bottom of the list.</li>
  <li>In the source list, select the branch where the documentation was pushed and next to it, the folder (<code class="language-plaintext highlighter-rouge">/docs</code> in our case unless) This is a requirement since Github only allows for a directory with either this same name or the repo’s root folder.</li>
  <li>After clicking <strong>Save</strong>, you should see the resulting link where the documentation is hosted now. Give a minute or two until Github deploys it.</li>
</ol>

<p><img src="https://geekingwithmauri.com/assets/posts/10_documentation/page.jpg" alt="final result" /></p>

<p>To check the state of the deployment, head over to:</p>

<figure class="highlight"><pre><code class="language-html" data-lang="html">https://github.com/[your_github_user]/[you_repo]/deployments/activity_log?environment=github-pages</code></pre></figure>
<p>The shortcut for this is the link next to the little rocket on the right side within your repo’s main page</p>

<p><img src="https://geekingwithmauri.com/assets/posts/10_documentation/pageDeploy.jpg" alt="page deploy" /></p>

<p>Now <a href="https://geekingwithmauri.github.io/MauriNet/">this is <strong>findable</strong> documentation for MauriNet</a> 🔎📜</p>

<h2 id="final-thoughts">Final thoughts</h2>

<p>As all things in life, the beauty of habit relies on consistency: start small if you’ve never done it before. Pick any function or class that you’re constantly working with and demands you to go through it every single time. That’s a great example of code that needs documentation -and potentially testing, but that’s a <a href="/swift/self-documented-code-with-flexible-swift-enums.html">subject of another post</a>-.</p>

<p>Trust me when I say to you your future self will be appretiate the effort as well as your productivity, overall levels of strees and teammates.</p>]]></content><author><name>Mauricio Chirino</name></author><category term="swift" /><category term="documentation" /><category term="CI" /><category term="work" /><category term="testing" /><category term="github" /><category term="unit tests" /><summary type="html"><![CDATA[How do we write useful documentation that's available beyond the IDE?]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://geekingwithmauri.com/assets/resources/logo.png" /><media:content medium="image" url="https://geekingwithmauri.com/assets/resources/logo.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry></feed>