<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Luis Ramos]]></title><description><![CDATA[Freelance mobile developer building native iOS and Android apps. I write about what I learn!]]></description><link>https://luisramos.dev</link><generator>GatsbyJS</generator><lastBuildDate>Fri, 17 Mar 2023 16:14:40 GMT</lastBuildDate><item><title><![CDATA[How to share resources in KMM]]></title><description><![CDATA[Building a mobile application will sooner or later put you in a position of bundling resource files. In the Kotlin Multiplatform Mobile…]]></description><link>https://luisramos.dev/how-to-share-resources-kmm</link><guid isPermaLink="false">https://luisramos.dev/how-to-share-resources-kmm</guid><pubDate>Wed, 06 Jul 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Building a mobile application will sooner or later put you in a position of bundling resource files. In the Kotlin Multiplatform Mobile world, this is still somewhat of a problem. I have a JSON file with some data I want to bundle in iOS and Android and in this post I will show you of a way to do it with a bit of configuration and some helper code.&lt;/p&gt;
&lt;h2&gt;Resources folder&lt;/h2&gt;
&lt;p&gt;The multiplatform shared library already has a structure folder for resources, so I’ve placed the json file in the &lt;code class=&quot;language-text&quot;&gt;src/commonMain/resources&lt;/code&gt; folder.&lt;/p&gt;
&lt;p&gt;The shared resources folder will not be added automatically to the Android library so add this to the shared android library &lt;code class=&quot;language-text&quot;&gt;build.gradle&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;kotlin&quot;&gt;&lt;pre class=&quot;language-kotlin&quot;&gt;&lt;code class=&quot;language-kotlin&quot;&gt;android &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
    sourceSets&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;main&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;resources&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setSrcDirs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;listOf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;src/androidMain/resources&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;src/commonMain/resources&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// &amp;lt;-- add the commonMain Resources&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now the files in that folder will be bundled in the library and available in the Android app!&lt;/p&gt;
&lt;p&gt;But what about iOS? Well, the solution I have is a bit awkward but it works! Add the &lt;code class=&quot;language-text&quot;&gt;src/commonMain/resources&lt;/code&gt; folder to the Xcode project. You can either drag it in, or right click where you want it and select &quot;Add Files to...&quot;. Make sure to deselect &quot;Copy items if needed&quot;, select &quot;Create groups&quot;, and add them to the targets you need.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 621px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/ca6620e7795b37e14e59005cad45d2b0/3075e/xcode-add-folder.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 82.94117647058825%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAARCAYAAADdRIy+AAAACXBIWXMAAAsTAAALEwEAmpwYAAAClElEQVQ4y5WUyW/bRhSHefYhCZxN4ixcRFIiJdEiRdKUtTmVCzux0yaAc8ghueScXAMkhwA59N4WTfvffsVMJbdNa9Q9fJjBvAXv/ebNOIM0pSgKS1mWVFXFQZ4TRDHToxVte0jTNNZWlgVl1RJlFWEYMBgMiOKYXhQRRRFhGOIM0z5NUzObzVgul6xWa9rDhqCfsjh7yunpKevjRxzNFywXC47Wp0T5itCT5OMRw2xAEvfoJxFRL8DxixlZPkErRbfbpdPp2FVKQW/acDCMqSYZvriP++A28sEttO8RTVr8XorwYqTft6ggwUnnJ2zOzjnZfGMrHA5HZFmKDiJ6l+9pv3/H/NlbVP2Ce/lzOqPHiNEaefkF+d1v7J/9zN7mR/a+/ZU7y484o+WG+fqY6Va/aVXR1FOCeIj/+icOX3/h+M0veE9+YG/xiTvLD6ikoDcqiQdG6wzpxyg/QfkRjux2EcJFKYWU0rZqVq0VYZbRDyVJKAnkfXx3H1/cRSuBliZGoJXE0zsUjtLa3uJisaAop+STktpUOv3jtg8mBVJplPZQ2t+i0VvUVzjmquu6YVoWjMuGSXvMerW0ehZFSZIkBEFgO1AmsV2vxzHOJiiOI6KkTzY5tHqWW+q6Jk1T6+x53lVl1+GYgczzA8qiYJQXeFmDcF2EEBbXdbea6hvhGOc/A43Y4l8d/6tVg034t0DTkuddGf8PVwl3m78muUk1N6tQ6xsJ/09f05lvu7Mamvdr2Glp3vIu4DrM4Bs/1xVo8RDV2ccTD3HMF9S2rWU8HtshN3tz+9dpac6NvW1nVGWBV5zQW79CT05w8jzn4uKC8/Nzm8x8V2ZvZnM3Ll/rZM6N3cRtHq3R85dkl59RRy/5HVUY7v7OLpjVAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;xcode-add-folder.png&quot;
        title=&quot;xcode-add-folder.png&quot;
        src=&quot;/static/ca6620e7795b37e14e59005cad45d2b0/3075e/xcode-add-folder.png&quot;
        srcset=&quot;/static/ca6620e7795b37e14e59005cad45d2b0/04472/xcode-add-folder.png 170w,
/static/ca6620e7795b37e14e59005cad45d2b0/9f933/xcode-add-folder.png 340w,
/static/ca6620e7795b37e14e59005cad45d2b0/3075e/xcode-add-folder.png 621w&quot;
        sizes=&quot;(max-width: 621px) 100vw, 621px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;A drawback of this is that we will need to add new resource files manually in Xcode. We can’t use a reference folder since we need those files to be individually added in the “Copy Bundle Resources” step. But I think this is totally worth it, since in my experience we spend more time editing the existing resources than creating new ones.&lt;/p&gt;
&lt;div class=&quot;blockquote info&quot;&gt;
   If you have a lot of resources you need to add, maybe you could look into a Run Script step that does that work!
&lt;/div&gt;
&lt;p&gt;Now how do we access the resources at runtime?&lt;/p&gt;
&lt;h2&gt;Access the resources&lt;/h2&gt;
&lt;p&gt;To read the resources, create a &lt;code class=&quot;language-text&quot;&gt;ResourceReader&lt;/code&gt; class with actual implementations for iOS and Android:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;kotlin&quot;&gt;&lt;pre class=&quot;language-kotlin&quot;&gt;&lt;code class=&quot;language-kotlin&quot;&gt; &lt;span class=&quot;token keyword&quot;&gt;expect&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; ResourceReader &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;readResource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; String&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; String
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Android resources are available via the class loader&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;actual&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; ResourceReader &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;actual&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;readResource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; String&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; String &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;
        javaClass&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;classLoader&lt;span class=&quot;token operator&quot;&gt;!!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getResourceAsStream&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; stream &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;
            &lt;span class=&quot;token function&quot;&gt;InputStreamReader&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;stream&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; reader &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;
                reader&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;readText&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Kotlin/Native resources will be in the Bundle!&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;actual&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; ResourceReader &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; bundle&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; NSBundle &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; NSBundle&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;bundleForClass&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;BundleMarker&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;actual&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;readResource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; String&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; String &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;filename&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; type&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; lastPeriodIndex &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;lastIndexOf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token char&quot;&gt;&apos;.&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;to&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;drop&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;..&lt;/span&gt;Int&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;MAX_VALUE &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                name&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;take&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;lastPeriodIndex&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;to&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;drop&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;lastPeriodIndex &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                name &lt;span class=&quot;token keyword&quot;&gt;to&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; path &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; bundle&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;pathForResource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;filename&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; type&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;?:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Couldn&apos;t get path of &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token expression&quot;&gt;name&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; (parsed as: &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token expression&quot;&gt;&lt;span class=&quot;token function&quot;&gt;listOfNotNull&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;filename&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;type&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;joinToString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;.&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;)&quot;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; memScoped &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; errorPtr &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; alloc&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;ObjCObjectVar&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;NSError&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

            NSString&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;stringWithContentsOfFile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
                path&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                encoding &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; NSUTF8StringEncoding&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                error &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; errorPtr&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ptr
            &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;?:&lt;/span&gt; run &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token function&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Couldn&apos;t load resource: &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token expression&quot;&gt;name&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;. Error: &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token expression&quot;&gt;errorPtr&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;localizedDescription&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; - &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token expression&quot;&gt;errorPtr&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; BundleMarker &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;NSObject&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;companion&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;NSObjectMeta&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;These implementations are from the &lt;a href=&quot;https://github.dev/touchlab/DroidconKotlin&quot;&gt;DroidconKotlin repo&lt;/a&gt;, which I recommend you take a look at because it has a lot of cool solutions for common KMM problems.&lt;/p&gt;
&lt;h2&gt;Wrap up&lt;/h2&gt;
&lt;p&gt;To read the files, call &lt;code class=&quot;language-text&quot;&gt;ResourceReader().readResource(&quot;timezones.json&quot;)&lt;/code&gt; and you will get a string with the contents of the file.&lt;/p&gt;
&lt;p&gt;With the resources always in the same folder (with the small caveat of having to manually add new files in Xcode) you will have an easier time managing them and making them work for you across both platforms.&lt;/p&gt;
&lt;p&gt;While researching for this post, I found the &lt;a href=&quot;https://github.com/icerockdev/moko-resources&quot;&gt;moko-resources&lt;/a&gt; library but it is way to opinionated for my use case and it does much more than I needed. Also, if you are looking to share resources across multiplaform tests for your shared library &lt;a href=&quot;https://developer.squareup.com/blog/kotlin-multiplatform-shared-test-resources/&quot;&gt;this blog post by Victoria&lt;/a&gt; has a really nice solution that I used for my testing setup.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Hierarchy structure in KMM: Sharing platform code between iOS and MacOS]]></title><description><![CDATA[Recently I came across the hierarchy structure feature, a Kotlin Mobile Multiplatform setting that enables you to share platform specific…]]></description><link>https://luisramos.dev/hierarchy-structure-in-kmm</link><guid isPermaLink="false">https://luisramos.dev/hierarchy-structure-in-kmm</guid><pubDate>Mon, 13 Sep 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Recently I came across the hierarchy structure feature, a Kotlin Mobile Multiplatform setting that enables you to share platform specific code between similar platforms. In this post I will explain what this useful feature does and help you set it up.&lt;/p&gt;
&lt;p&gt;As an example, lets take one of my recent KMM side projects. It has 3 target platforms: Android, iOS and MacOS. In it, I have the need to generate a UUID as an identifier in the shared code. This is something not available in Kotlin standard library, but available in each target platform.&lt;/p&gt;
&lt;p&gt;There are libraries that provide this functional already, but it is something very simple to implement by ourselves using the &lt;code class=&quot;language-text&quot;&gt;expect&lt;/code&gt;/&lt;code class=&quot;language-text&quot;&gt;actual&lt;/code&gt; construct:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;kotlin&quot;&gt;&lt;pre class=&quot;language-kotlin&quot;&gt;&lt;code class=&quot;language-kotlin&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// commonMain/generateUUID.kt&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;expect&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;generateUUID&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; String

&lt;span class=&quot;token comment&quot;&gt;// androidMain&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;actual&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;generateUUID&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; String &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; UUID&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;randomUUID&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// iosMain&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;actual&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;generateUUID&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; String &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;NSUUID&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;UUIDString

&lt;span class=&quot;token comment&quot;&gt;// macOSMain&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;actual&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;generateUUID&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; String &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;NSUUID&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;UUIDString&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The Android implementation uses Java &lt;code class=&quot;language-text&quot;&gt;UUID&lt;/code&gt; class. The code for iOS and MacOS uses Foundation &lt;code class=&quot;language-text&quot;&gt;NSUUID&lt;/code&gt; class. The way the project is setup right now, we end up with a duplicate &lt;code class=&quot;language-text&quot;&gt;actual&lt;/code&gt; declaration for the &lt;code class=&quot;language-text&quot;&gt;NSUUID&lt;/code&gt; call.&lt;/p&gt;
&lt;p&gt;This is exactly the type of problem the hierarchy structure feature solves. You can declare an intermediate source set that holds the shared platform code which the target platforms will depend on.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;https://kotlinlang.org/docs/mpp-share-on-platforms.html#share-code-on-similar-platforms&quot;&gt;documentation page&lt;/a&gt; explains how to enable support for this. You have to add the following option to &lt;code class=&quot;language-text&quot;&gt;gradle.properties&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;kotlin&quot;&gt;&lt;pre class=&quot;language-kotlin&quot;&gt;&lt;code class=&quot;language-kotlin&quot;&gt;kotlin&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;mpp&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;enableGranularSourceSetsMetadata&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You can then use the target shortcuts the KMM plugin gives you or, in our case, you have to setup the targets manually. The shortcuts available don&apos;t actually cover our case of shared code between MacOS and iOS.&lt;/p&gt;
&lt;p&gt;Here is the sources in &lt;code class=&quot;language-text&quot;&gt;build.gradle.kts&lt;/code&gt; for the shared module, named &lt;code class=&quot;language-text&quot;&gt;nativeMain&lt;/code&gt;. Note the &lt;code class=&quot;language-text&quot;&gt;creating {}&lt;/code&gt; call, instead of just &lt;code class=&quot;language-text&quot;&gt;getting()&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;kotlin&quot;&gt;&lt;pre class=&quot;language-kotlin&quot;&gt;&lt;code class=&quot;language-kotlin&quot;&gt;kotlin &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;android&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token function&quot;&gt;ios&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;macosX64&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;macos&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    cocoapods &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        summary &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Shared Framework&quot;&lt;/span&gt;&lt;/span&gt;
        homepage &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Shared&quot;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    sourceSets &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; commonMain &lt;span class=&quot;token keyword&quot;&gt;by&lt;/span&gt; getting
        &lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; androidMain &lt;span class=&quot;token keyword&quot;&gt;by&lt;/span&gt; getting
        &lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; iosMain &lt;span class=&quot;token keyword&quot;&gt;by&lt;/span&gt; getting
        &lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; macosMain &lt;span class=&quot;token keyword&quot;&gt;by&lt;/span&gt; getting
        &lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; nativeMain &lt;span class=&quot;token keyword&quot;&gt;by&lt;/span&gt; creating &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token function&quot;&gt;dependsOn&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;commonMain&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            iosMain&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;dependsOn&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            macosMain&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;dependsOn&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;With this setup, our project is now using an hierarchy structure for our MacOS and iOS targets, and we no longer need to duplicate our actual declarations:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;kotlin&quot;&gt;&lt;pre class=&quot;language-kotlin&quot;&gt;&lt;code class=&quot;language-kotlin&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// generateUUID.kt&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;expect&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;generateUUID&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; String

&lt;span class=&quot;token comment&quot;&gt;// androidMain&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;actual&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;generateUUID&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; String &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; UUID&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;randomUUID&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// nativeMain&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;actual&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;generateUUID&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; String &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;NSUUID&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;UUIDString&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Hope this information helps with your KMM setup, it really helped me clean up some code duplication in my project.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Marrying KMM and Swift with Sourcery]]></title><description><![CDATA[My bet is that Kotlin Multiplatform Mobile is here to stay. It offers a great way to build mobile apps natively that share code, without…]]></description><link>https://luisramos.dev/marrying-kmm-and-swift-with-sourcery</link><guid isPermaLink="false">https://luisramos.dev/marrying-kmm-and-swift-with-sourcery</guid><pubDate>Fri, 02 Jul 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;My bet is that Kotlin Multiplatform Mobile is here to stay. It offers a great way to build mobile apps natively that share code, without compromising the best each platform has to offer. Of course, it comes with its own trade-offs.&lt;/p&gt;
&lt;p&gt;Let me tell you how you can smooth out some of the quirks using the Swift meta-programming library &lt;a href=&quot;https://github.com/krzysztofzablocki/Sourcery&quot;&gt;Sourcery&lt;/a&gt;!&lt;/p&gt;
&lt;h2&gt;The problem: Kotlin extensions in Swift&lt;/h2&gt;
&lt;p&gt;The &lt;a href=&quot;https://kotlinlang.org/docs/native-objc-interop.html&quot;&gt;interoperability&lt;/a&gt; between Swift and Kotlin uses Objective-C as a medium, and it doesn&apos;t support Kotlin extensions has one would expect. An extension function like:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;kotlin&quot;&gt;&lt;pre class=&quot;language-kotlin&quot;&gt;&lt;code class=&quot;language-kotlin&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// file name is RocketExtensions.kt&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;fun&lt;/span&gt; Rocket&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;launch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Boolean&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Will translate into Swift like this:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;kotlin&quot;&gt;&lt;pre class=&quot;language-kotlin&quot;&gt;&lt;code class=&quot;language-kotlin&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; RocketKt &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; func &lt;span class=&quot;token function&quot;&gt;launch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;_ receiver&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Rocket&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Bool
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// How we expect to use it&lt;/span&gt;
rocket&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;launch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// How we actually have to use it&lt;/span&gt;
let rocket &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Rocket&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
RocketExtensionsKt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;launch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;rocket&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The way Kotlin/Native generates the Objective-C header files for our extensions means they are accessible in Swift via a &lt;code class=&quot;language-text&quot;&gt;RocketExtensionsKt&lt;/code&gt; class. Note that the class matches the file name, not the class it is actually extending, which is a bummer. Nothing we can do for now in the Kotlin side, until pure Swift modules are supported.&lt;/p&gt;
&lt;h2&gt;The solution: Sourcery!&lt;/h2&gt;
&lt;p&gt;One library that you need to be aware if you are building iOS apps is &lt;a href=&quot;https://github.com/krzysztofzablocki/Sourcery&quot;&gt;Sourcery&lt;/a&gt; from &lt;a href=&quot;https://twitter.com/merowing_&quot;&gt;@merowing_&lt;/a&gt;. The description says &quot;Meta-programming for Swift, stop writing boilerplate code.&quot; and I am totally there for it.&lt;/p&gt;
&lt;p&gt;I have only started to use it recently, but the flexibility you gain is truly incredible. How does this help with our extensions situation? Well, we can write a template that Sourcery can use to generate actual extensions in Swift, shadowing the extension classes from our shared framework.&lt;/p&gt;
&lt;div class=&quot;blockquote info&quot;&gt;
If you are new to Sourcery, you can follow the &lt;a href=&quot;https://github.com/krzysztofzablocki/Sourcery#installation&quot;&gt;installation guide&lt;/a&gt; to set it up, and I found &lt;a href=&quot;https://www.hackingwithswift.com/articles/85/introduction-to-sourcery&quot;&gt;this article&lt;/a&gt; by Paul Hudson to be a great introduction.
&lt;/div&gt;
&lt;h3&gt;Speed bump&lt;/h3&gt;
&lt;p&gt;Sourcery only works with Swift files and a KMM project will generate extensions in a framework with Obj-C headers. So we need to make Sourcery aware of our shared framework code!&lt;/p&gt;
&lt;p&gt;If you open up an Obj-C file in Xcode while you are in a Swift project, Xcode will show you a generated interface for the Obj-C file to help you out. We can actually use the Swift CLI to the same effect, and generate a Swift interface for our shared framework that can be feed into Sourcery.&lt;/p&gt;
&lt;p&gt;Add a &quot;Run Script Phase&quot; to your Xcode target with this bit of code:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;import shared&lt;span class=&quot;token entity&quot; title=&quot;\n&quot;&gt;\n&lt;/span&gt;:type lookup shared&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
	xcrun &lt;span class=&quot;token parameter variable&quot;&gt;--sdk&lt;/span&gt; macosx swift -F&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;/shared/build/cocoapods/framework/ &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
	&lt;span class=&quot;token function&quot;&gt;tail&lt;/span&gt; -n+2 &lt;span class=&quot;token operator&quot;&gt;&gt;|&lt;/span&gt; ./Sources/Shared/Shared.swift&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Here is what the script does:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;We &lt;code class=&quot;language-text&quot;&gt;echo&lt;/code&gt; to the Swift CLI that we want to do a type lookup of the &lt;code class=&quot;language-text&quot;&gt;shared&lt;/code&gt; framework. Replace &lt;code class=&quot;language-text&quot;&gt;shared&lt;/code&gt; with your shared code framework.&lt;/li&gt;
&lt;li&gt;Use &lt;code class=&quot;language-text&quot;&gt;xcrun&lt;/code&gt; to call &lt;code class=&quot;language-text&quot;&gt;swift&lt;/code&gt; and generate the interface file. We also take care include a path to our framework in the framework search paths using the &lt;code class=&quot;language-text&quot;&gt;-F&lt;/code&gt; flag.&lt;/li&gt;
&lt;li&gt;Save the output to a Swift file, somewhere where Sourcery can read it.&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;blockquote alert&quot;&gt;
Make note of adding this step before the Sourcery step, otherwise Sourcery might only pick up new types on the second build. Also note the &lt;code class=&quot;language-text&quot;&gt;--sdk macosx&lt;/code&gt; flag. Running this inside Xcode will make `swift` default to whatever arch you are building, and if it is iOS it won&apos;t work.
&lt;/div&gt;
&lt;h3&gt;Write the Stencil template&lt;/h3&gt;
&lt;p&gt;Now, we just need to write a template that makes use of our shared framework types to create the extensions we want. Here is what I came up with:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;swift&quot;&gt;&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; shared

&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt; macro paramsWithoutSelf method &lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; param &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; method&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;parameters &lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; forloop&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;first &lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; param&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;argumentLabel &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;param&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;typeName&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; not forloop&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;last &lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt; endif&lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt; endif &lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt; endfor &lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt; endmacro &lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt; macro argsWithoutSelf method &lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; param &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; method&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;parameters &lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; forloop&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;first &lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
			&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; param&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;argumentLabel &lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; param&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;argumentLabel &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt; endif &lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; forloop&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt; endif &lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
			&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; param&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;argumentLabel &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; param&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; not forloop&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;last &lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt; endif&lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt; endif &lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt; endfor &lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt; endmacro &lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; type &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; types&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;all &lt;span class=&quot;token keyword&quot;&gt;where&lt;/span&gt; type&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;contains&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Kt&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; method &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; type&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;methods &lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; method&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;parameters&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;count &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;set&lt;/span&gt; isObsolete &lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; attr &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; method&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;attributes &lt;span class=&quot;token keyword&quot;&gt;where&lt;/span&gt; attr&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;contains&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;available&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;attr&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt; endfor &lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt; endset &lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; not isObsolete &lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;set&lt;/span&gt; extensionName &lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; method&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;parameters&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;first&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;typeName&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;contains&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;?&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Optional&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;where&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Wrapped&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; method&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;parameters&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;first&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;typeName&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;replace&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;?&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; method&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;parameters&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;first&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;typeName &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt; endif &lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt; endset &lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;extension&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; extensionName &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; method&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;shortName &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt; call paramsWithoutSelf method &lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; method&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;returnTypeName &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; type&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; method&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;shortName &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt; call argsWithoutSelf method &lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt; endif &lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt; endif &lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt; endfor &lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt; endfor &lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;blockquote info&quot;&gt;
Sentcil is pretty hard to read for the uninitiated. But don&apos;t be intimidated, you can checkout the &lt;a href=&quot;https://stencil.fuller.li/en/latest/templates.html#&quot;&gt;language overview&lt;/a&gt; to get a good grasp of what it can do, and refer to the &lt;a href=&quot;https://cdn.rawgit.com/krzysztofzablocki/Sourcery/master/docs/writing-templates.html&quot;&gt;Sourcery documentation&lt;/a&gt; to get an idea of what is available to you.
&lt;/div&gt;
&lt;p&gt;This template makes some safe assumptions regarding our shared types:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A Kotlin extension class name will always end with &quot;Kt&quot; in Swift&lt;/li&gt;
&lt;li&gt;The first parameter of the Kotlin extension method is the class we want to extend&lt;/li&gt;
&lt;li&gt;For the previous to work, we ignore methods without parameters, like &lt;code class=&quot;language-text&quot;&gt;init()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;We can skip methods tagged with &lt;code class=&quot;language-text&quot;&gt;obsoleted&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The other thing of note is that &lt;code class=&quot;language-text&quot;&gt;Optional&lt;/code&gt; types are extended correctly with a where clause, otherwise we would generate &lt;code class=&quot;language-text&quot;&gt;extension Rocket? {}&lt;/code&gt; which doesn&apos;t compile.&lt;/p&gt;
&lt;p&gt;Using our previous &lt;code class=&quot;language-text&quot;&gt;Rocket&lt;/code&gt; extension as an example, here is what we end up with:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;swift&quot;&gt;&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Swift generate code&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; shared

&lt;span class=&quot;token keyword&quot;&gt;extension&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Rocket&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;launch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Bool&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token class-name&quot;&gt;RocketKt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;launch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
			&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I am sure the template can be improved, but for now it covers all my extensions without issues and saves me from writing all this boilerplate.&lt;/p&gt;
&lt;h3&gt;The magic doesn&apos;t stop here...&lt;/h3&gt;
&lt;p&gt;Sourcery fits really well bridging the Kotlin to Swift gap. Its usage doesn&apos;t stop with the extensions! Another example of how I use it is with a shared theme configuration.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;kotlin&quot;&gt;&lt;pre class=&quot;language-kotlin&quot;&gt;&lt;code class=&quot;language-kotlin&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;object&lt;/span&gt; Theme &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; backgroundColor &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0xFFFF00FF&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; salmonColor &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0xFFFF0000&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; shadowColor &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0xFF00000&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;For iOS I wrote a template that creates an extension to &lt;code class=&quot;language-text&quot;&gt;SwiftUI.Color&lt;/code&gt; with the theme colors. Now every time you add a new color in the &lt;code class=&quot;language-text&quot;&gt;Theme&lt;/code&gt; object, you automatically get the extensions in Swift. We end up with a file like:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;swift&quot;&gt;&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SwiftUI&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; shared

&lt;span class=&quot;token keyword&quot;&gt;extension&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Color&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; backgroundColor&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Color&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token class-name&quot;&gt;Theme&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;backgroundColor&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toColor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Here is how you use it!&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DotView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; body&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;some&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;View&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token class-name&quot;&gt;Circle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
			&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fill&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;backgroundColor&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Here is the Stencil template:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;kotlin&quot;&gt;&lt;pre class=&quot;language-kotlin&quot;&gt;&lt;code class=&quot;language-kotlin&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; shared
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; SwiftUI

&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; type &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; types&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;all &lt;span class=&quot;token keyword&quot;&gt;where&lt;/span&gt; type&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Theme&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
extension Color &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; variable &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; type&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;variables &lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
	static &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; variable&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Color &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; type&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; variable&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toColor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

	&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt; endfor &lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt; endfor &lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;We made use of a Sourcery template to generate code that makes our Kotlin extensions actually extend the correct Swift classes. We also used the Swift CLI to generate an interface of our shared framework that Sourcery could read. And we set it up so it runs every time Xcode builds, so changes in the shared framework are automatically propagated to Swift.&lt;/p&gt;
&lt;p&gt;Integrating Sourcery early into a KMM project pays off in the short and in the long run. With this setup, you avoid writing a lot of boilerplate code and bridge the gap between Kotlin and Swift a bit better. Best part is that you can keep adding templates to fit your usage and make it easy to keep switching contexts.&lt;/p&gt;
&lt;p&gt;Hope you enjoyed this post and that it makes your life a little bit easier in the KMM world.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Xcode Format and Save using SwiftFormat and Automator]]></title><description><![CDATA[Lost are the days where we could modify and extend Xcode to do our bidding (RIP Alcatraz). We have some room to work with source editor…]]></description><link>https://luisramos.dev/xcode-format-and-save</link><guid isPermaLink="false">https://luisramos.dev/xcode-format-and-save</guid><pubDate>Tue, 29 Jun 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Lost are the days where we could modify and extend Xcode to do our bidding (RIP Alcatraz). We have some room to work with source editor extensions, but adding a macro that formats our Swift code and then saves the file is out of reach. Or is it?&lt;/p&gt;
&lt;p&gt;Via a weird combination of the &lt;a href=&quot;https://github.com/nicklockwood/SwiftFormat&quot;&gt;SwiftFormat&lt;/a&gt; source editor extension, Automator, and Keyboard shortcuts I can now format and save my file by pressing the blessed Cmd+S shortcut.&lt;/p&gt;
&lt;p&gt;Here is how it works:&lt;/p&gt;
&lt;h2&gt;Step 1: setup SwiftFormat&lt;/h2&gt;
&lt;p&gt;If you use &lt;code class=&quot;language-text&quot;&gt;brew&lt;/code&gt; like me, run these commands to install SwiftFormat. Otherwise, check their &lt;a href=&quot;https://github.com/nicklockwood/SwiftFormat#command-line-tool&quot;&gt;Github page&lt;/a&gt; for installation instructions.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;brew &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; swiftformat
brew &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;--cask&lt;/span&gt; swiftformat-for-xcode
&lt;span class=&quot;token function&quot;&gt;open&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;/Applications/SwiftFormat For Xcode.app&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Last command will open the &quot;SwiftFormat For Xcode&quot; app, so it can install the source editor extension. Don&apos;t forget to also restart Xcode.&lt;/p&gt;
&lt;p&gt;This extension will place a menu item that formats our file, under &quot;Editor&quot; &gt; &quot;SwiftFormat&quot; &gt; &quot;Format File&quot;. This is important for our next step!&lt;/p&gt;
&lt;h2&gt;Step 2: automate with Automator&lt;/h2&gt;
&lt;p&gt;Open up the lovely Automator, and create a Quick Action.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 680px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/4b69bfc263fa6be6a872ac1c7bc9016f/9cea8/automator_1.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 77.64705882352942%; position: relative; bottom: 0; left: 0; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Create an Automator Quick Action&quot;
        title=&quot;Create an Automator Quick Action&quot;
        src=&quot;/static/4b69bfc263fa6be6a872ac1c7bc9016f/c5bb3/automator_1.png&quot;
        srcset=&quot;/static/4b69bfc263fa6be6a872ac1c7bc9016f/04472/automator_1.png 170w,
/static/4b69bfc263fa6be6a872ac1c7bc9016f/9f933/automator_1.png 340w,
/static/4b69bfc263fa6be6a872ac1c7bc9016f/c5bb3/automator_1.png 680w,
/static/4b69bfc263fa6be6a872ac1c7bc9016f/b12f7/automator_1.png 1020w,
/static/4b69bfc263fa6be6a872ac1c7bc9016f/9cea8/automator_1.png 1278w&quot;
        sizes=&quot;(max-width: 680px) 100vw, 680px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Next add a &quot;Run AppleScript&quot; action, and paste in the following code:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;applescript&quot;&gt;&lt;pre class=&quot;language-applescript&quot;&gt;&lt;code class=&quot;language-applescript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;on&lt;/span&gt; run &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;input&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; parameters&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;tell&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;application&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;System Events&quot;&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;tell&lt;/span&gt; process &lt;span class=&quot;token string&quot;&gt;&quot;Xcode&quot;&lt;/span&gt;
			&lt;span class=&quot;token keyword&quot;&gt;set&lt;/span&gt; frontmost &lt;span class=&quot;token keyword&quot;&gt;to&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;true&lt;/span&gt;
			&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; menu item &lt;span class=&quot;token string&quot;&gt;&quot;Format File&quot;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; menu &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; menu item &lt;span class=&quot;token string&quot;&gt;&quot;SwiftFormat&quot;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; menu &lt;span class=&quot;token string&quot;&gt;&quot;Editor&quot;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; menu bar &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; exists &lt;span class=&quot;token keyword&quot;&gt;then&lt;/span&gt;
				click menu item &lt;span class=&quot;token string&quot;&gt;&quot;Format File&quot;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; menu &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; menu item &lt;span class=&quot;token string&quot;&gt;&quot;SwiftFormat&quot;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; menu &lt;span class=&quot;token string&quot;&gt;&quot;Editor&quot;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; menu bar &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;
			&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;
			click menu item &lt;span class=&quot;token string&quot;&gt;&quot;Save&quot;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; menu &lt;span class=&quot;token string&quot;&gt;&quot;File&quot;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; menu bar &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;tell&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;tell&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; input
&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt; run&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The script will do the following:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Check to see if &quot;SwiftFormat&quot; &gt; &quot;Format File&quot; menu item exists in Xcode&lt;/li&gt;
&lt;li&gt;If it does exist then click &quot;Format File&quot;, otherwise do nothing&lt;/li&gt;
&lt;li&gt;Click &quot;Save&quot;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Here is how the Quick Action looks. I&apos;ve changed the &quot;Workflow receives&quot; to &quot;no input&quot; and &quot;in&quot; to &quot;Xcode&quot;.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 680px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/b1552b49e600ad62694e7871ba364775/eb2ef/automator_2.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 65.88235294117646%; position: relative; bottom: 0; left: 0; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Add a Run AppleScript action&quot;
        title=&quot;Add a Run AppleScript action&quot;
        src=&quot;/static/b1552b49e600ad62694e7871ba364775/c5bb3/automator_2.png&quot;
        srcset=&quot;/static/b1552b49e600ad62694e7871ba364775/04472/automator_2.png 170w,
/static/b1552b49e600ad62694e7871ba364775/9f933/automator_2.png 340w,
/static/b1552b49e600ad62694e7871ba364775/c5bb3/automator_2.png 680w,
/static/b1552b49e600ad62694e7871ba364775/b12f7/automator_2.png 1020w,
/static/b1552b49e600ad62694e7871ba364775/eb2ef/automator_2.png 1324w&quot;
        sizes=&quot;(max-width: 680px) 100vw, 680px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Save the Quick Action. Automator will place it directly into a macOS &quot;Services&quot; folder, and that will make it available in the &quot;Xcode&quot; &gt; &quot;Services&quot; menu.&lt;/p&gt;
&lt;p&gt;This action requires access to &quot;Accessibility&quot; permissions. If you try to run it via Automator you will probably get a permission related error. We will address this further down the post!&lt;/p&gt;
&lt;p&gt;And there you go! A Format and Save macro. Now the last step:&lt;/p&gt;
&lt;h2&gt;Step 3: Add a Keyboard shortcut&lt;/h2&gt;
&lt;p&gt;Go to &quot;System Preferences&quot; &gt; &quot;Keyboard&quot; and pick the &quot;Shortcuts&quot; tab. On the sidebar, select &quot;App Shortcuts&quot;. Add a new shortcut, the &quot;Application&quot; is XCode, the &quot;Menu Title&quot; is the name of our Quick Action (in my case XcodeFormatAndSave), and the &quot;Keyboard Shortcut&quot; is Cmd+S!&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 680px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/0767e45797d18dc065205ed13f4f3140/a1792/shortcuts_1.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 90.58823529411765%; position: relative; bottom: 0; left: 0; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Add keyboard shortcut&quot;
        title=&quot;Add keyboard shortcut&quot;
        src=&quot;/static/0767e45797d18dc065205ed13f4f3140/c5bb3/shortcuts_1.png&quot;
        srcset=&quot;/static/0767e45797d18dc065205ed13f4f3140/04472/shortcuts_1.png 170w,
/static/0767e45797d18dc065205ed13f4f3140/9f933/shortcuts_1.png 340w,
/static/0767e45797d18dc065205ed13f4f3140/c5bb3/shortcuts_1.png 680w,
/static/0767e45797d18dc065205ed13f4f3140/a1792/shortcuts_1.png 780w&quot;
        sizes=&quot;(max-width: 680px) 100vw, 680px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 680px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/c05ac3ba641ed888889be826e5ad4443/a1792/shortcuts_2.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 90.58823529411765%; position: relative; bottom: 0; left: 0; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Keyboard shortcut settings&quot;
        title=&quot;Keyboard shortcut settings&quot;
        src=&quot;/static/c05ac3ba641ed888889be826e5ad4443/c5bb3/shortcuts_2.png&quot;
        srcset=&quot;/static/c05ac3ba641ed888889be826e5ad4443/04472/shortcuts_2.png 170w,
/static/c05ac3ba641ed888889be826e5ad4443/9f933/shortcuts_2.png 340w,
/static/c05ac3ba641ed888889be826e5ad4443/c5bb3/shortcuts_2.png 680w,
/static/c05ac3ba641ed888889be826e5ad4443/a1792/shortcuts_2.png 780w&quot;
        sizes=&quot;(max-width: 680px) 100vw, 680px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;And there you go! If you go to Xcode and try to save a Swift file, it will run SwiftFormat before it saves.&lt;/p&gt;
&lt;p&gt;Actually, it will probably throw an error. This action needs &quot;Accessibility&quot; permissions, so you will need to navigate to &quot;System Preferences&quot; &gt; &quot;Security &amp;#x26; Privacy&quot; and give permission to Xcode.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 680px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/3e2790b7ebe46fef2ab3fe46fd20cf9f/a1792/security_permissions.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 89.41176470588235%; position: relative; bottom: 0; left: 0; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Accessibility permissions&quot;
        title=&quot;Accessibility permissions&quot;
        src=&quot;/static/3e2790b7ebe46fef2ab3fe46fd20cf9f/c5bb3/security_permissions.png&quot;
        srcset=&quot;/static/3e2790b7ebe46fef2ab3fe46fd20cf9f/04472/security_permissions.png 170w,
/static/3e2790b7ebe46fef2ab3fe46fd20cf9f/9f933/security_permissions.png 340w,
/static/3e2790b7ebe46fef2ab3fe46fd20cf9f/c5bb3/security_permissions.png 680w,
/static/3e2790b7ebe46fef2ab3fe46fd20cf9f/a1792/security_permissions.png 780w&quot;
        sizes=&quot;(max-width: 680px) 100vw, 680px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Now you can let Xcode take care of the formatting. You are welcome!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[A year of Ler]]></title><description><![CDATA[A year ago, I published Ler - a feed aggregator app for Android. After releasing it, my plan was to clean the code to open source it, and…]]></description><link>https://luisramos.dev/a-year-of-ler</link><guid isPermaLink="false">https://luisramos.dev/a-year-of-ler</guid><pubDate>Mon, 26 Apr 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;A year ago, I published &lt;a href=&quot;https://luisramos.dev/ler-rss-aggregator-for-android&quot;&gt;Ler - a feed aggregator app&lt;/a&gt; for Android. After releasing it, my plan was to clean the code to open source it, and write posts about its architecture and how I code. Almost none of that happened but version 1.1 is out!&lt;/p&gt;
&lt;h2&gt;Recap&lt;/h2&gt;
&lt;p&gt;Not much writing happened in regards to the app, but I kept working on it. Not often, but enough to rewrite a lot of the old implementation.&lt;/p&gt;
&lt;p&gt;I replaced the usage of &lt;code class=&quot;language-text&quot;&gt;Fragment&lt;/code&gt; with a custom &lt;code class=&quot;language-text&quot;&gt;Screen&lt;/code&gt;s navigation implementation. Because, why not? Also, I ditched the XML layouts in favor of using regular Android views. Big props to &lt;a href=&quot;https://github.com/cashapp/contour&quot;&gt;Contour&lt;/a&gt;, a layout view for Android by CashApp, it really helped with that.&lt;/p&gt;
&lt;p&gt;I use this app as a test bed for new frameworks and libraries. These changes didn&apos;t add anything to the feature set, but they felt really good! And I learned how to build an app without relying on fragments.&lt;/p&gt;
&lt;p&gt;One thing I am still learning is how to cut scope so I can finish something. I had to give up on the idea of &quot;cleaning up&quot; the code base. By that I mean adding missing tests and restructure the files to make more sense to some other dev. I realized that I would not be able to muster the energy to do it, so the project is now &lt;a href=&quot;https://github.com/Orgmir/ler-android&quot;&gt;open source&lt;/a&gt; for everyone to check out.&lt;/p&gt;
&lt;h2&gt;Version 1.1 is out!&lt;/h2&gt;
&lt;p&gt;After a year, the new release of Ler comes with a new feature and a couple of fixes. You can &lt;a href=&quot;https://play.google.com/store/apps/details?id=app.luisramos.ler&quot;&gt;download it here&lt;/a&gt;, or check for an update if you are already a user.&lt;/p&gt;
&lt;p&gt;Changes since 1.0.2:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;New posts notification! You will now be notified when there are new posts for you to read&lt;/li&gt;
&lt;li&gt;New settings screen, where you can configure the new posts notification time (or disable it)&lt;/li&gt;
&lt;li&gt;You can now disable the subscriptions background refresh&lt;/li&gt;
&lt;li&gt;Fixed update URL for a subscription not getting parsed correctly&lt;/li&gt;
&lt;li&gt;Fixed post published date parsing, now uses the device locale (and it actually works)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Ding, there are new posts!&lt;/h2&gt;
&lt;p&gt;I have the notification set so it triggers about the time I wake up, that way I don&apos;t miss any new posts. It really helps me stay on top of things, and I can read posts while eating breakfast ;) You can set this to trigger at any time of day.&lt;/p&gt;
&lt;p&gt;The rate subscriptions refresh data changed as well, it was overly eager before and now I have set it to refresh every 12 hours. You can disable the refresh in the settings.&lt;/p&gt;
&lt;h2&gt;Happy reading!&lt;/h2&gt;
&lt;p&gt;I hope you like the new notification, it was surprisingly complicated to build. I almost gave up on it. A year ago I would have parked the idea before completion. But something I learned this year is how to keep at it, instead of waiting for the right motivation to come around.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Mint: A language for Single Page Applications]]></title><description><![CDATA[Android and iOS are my tech stacks of choice, both for work and pleasure, but I often keep tabs on what's going on on the web. This post is…]]></description><link>https://luisramos.dev/mint-language</link><guid isPermaLink="false">https://luisramos.dev/mint-language</guid><pubDate>Fri, 09 Apr 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Android and iOS are my tech stacks of choice, both for work and pleasure, but I often keep tabs on what&apos;s going on on the web.&lt;/p&gt;
&lt;p&gt;This post is about &lt;a href=&quot;https://www.mint-lang.com&quot;&gt;Mint&lt;/a&gt;, a domain specific type-safe language created to build single page web applications. It has all the necessary utilities that a SPA will use bundled in the framework: components, stores, router, and more! And it makes them part of the language, with specific keywords.&lt;/p&gt;
&lt;p&gt;Here is how you declare a component:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;component Main &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  style button &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; red&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; white&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;border&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  fun render &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Html &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;button:&lt;/span&gt;:button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
      &quot;Click ME!&quot;
    &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
  }
}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;There is a &lt;code class=&quot;language-text&quot;&gt;styles&lt;/code&gt; keyword that allows you to use CSS to style your components, and it will all be sandboxed properly per component.&lt;/p&gt;
&lt;p&gt;The syntax is similar to React, and the way it actually works underneath the hood is by offering React as a platform! It has a compiler and the build system will let you know of any errors you might have in your code. You are completely removed from the implementations specifics, and can focus on building your website.&lt;/p&gt;
&lt;p&gt;Here is how you could declare a store and use it in a component, from &lt;a href=&quot;https://www.mint-lang.com/guide/reference/components/connecting-stores&quot;&gt;Mint documentation&lt;/a&gt;:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// This is our store. You can have multiple stores per app&lt;/span&gt;
store Counter &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  state count &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Number &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;

  fun &lt;span class=&quot;token function&quot;&gt;setCount&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;count &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Number&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Promise&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;Never&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Void&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    next &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; count &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; count &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

component Main &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// Connecting the store to the component&lt;/span&gt;
  connect Counter exposing &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; count&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; setCount &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  fun &lt;span class=&quot;token function&quot;&gt;handleClick&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;event &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Html&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Event&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Promise&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;Never&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Void&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;setCount&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;count &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  fun &lt;span class=&quot;token function&quot;&gt;handleContextMenu&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;event &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Html&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Event&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Promise&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;Never&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Void&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;setCount&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  fun render &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Html &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;
      &lt;span class=&quot;token attr-name&quot;&gt;onContextMenu&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;handleContextMenu&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token attr-name&quot;&gt;onClick&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;handleClick&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;

      &amp;lt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Count: &quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; Number&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;count&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;&gt;

    &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Really like how easy to ready everything is. There is a lot more to it, and I strongly suggest you read through its &lt;a href=&quot;https://www.mint-lang.com/guide&quot;&gt;learning guide/documentation&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Double edged sword&lt;/h3&gt;
&lt;p&gt;I really enjoy the power of this abstraction. But like all software it comes as a trade-off. By abstracting away the platform, it cuts us off from the ever expanding JavaScript ecosystem.&lt;/p&gt;
&lt;p&gt;It&apos;s package system only allows for packages with Mint code in them. There is no way to use an existing JavaScript library in Mint, without wrapping it.&lt;/p&gt;
&lt;p&gt;Mint has JavaScript interoperability, allowing you to declare standard JavaScript in your Mint code by surrounding it with backticks:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;fun &lt;span class=&quot;token function&quot;&gt;handleClick&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;event &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Html&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Event&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Void &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;alert(&quot;Hello&quot;)&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;But this interoperability only exists at the code level. There is no build step hooks, so an existing JavaScript library would need to be backticked and probably exposed as a &lt;code class=&quot;language-text&quot;&gt;module&lt;/code&gt; to be available in Mint world.&lt;/p&gt;
&lt;p&gt;Let&apos;s say if you want to integrate &lt;a href=&quot;https://tailwindcss.com/&quot;&gt;TailwindCSS&lt;/a&gt; into your Mint SPA. Tailwind hooks into your build preprocessor step, in fact it assumes you will have one. Without access to a preprocessor hook, the only option is to generate all Tailwind CSS and include it in the head, like &lt;a href=&quot;https://www.mint-lang.com/guide/recipes/using-third-party-css&quot;&gt;Mint&apos;s docs suggest&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;We could have our own script that parses the Mint files, looking for CSS classes, and then feed that to TailwindCSS. And then hook this with a file watcher. But at this point, you would have to run &lt;code class=&quot;language-text&quot;&gt;npm install &amp;amp;&amp;amp; mint install&lt;/code&gt; to start up the project, which means you are back into regular JavaScript land. Is this effort worth it? What if we want to use a date parsing library, or a JavaScript form library...&lt;/p&gt;
&lt;h3&gt;Still try it out!&lt;/h3&gt;
&lt;p&gt;Mint wants to do everything for you in the name of developer experience, but it cuts us away from web-dev JavaScript tools and environment. Enabling a strong developer ecosystem is what makes JavaScript one of the most popular environments to work in, and I think Mint should enable that if it wants to keep growing.&lt;/p&gt;
&lt;p&gt;Nevertheless, if you keep to its boundaries, building a SPA with Mint is a breeze!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Testing your Android ViewModel - with examples]]></title><description><![CDATA[An android application can get really complicated, so in this post I will give some examples on how I write and test . These days my usual…]]></description><link>https://luisramos.dev/testing-your-android-viewmodel</link><guid isPermaLink="false">https://luisramos.dev/testing-your-android-viewmodel</guid><pubDate>Fri, 19 Mar 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;An android application can get really complicated, so in this post I will give some examples on how I write and test &lt;code class=&quot;language-text&quot;&gt;ViewModels&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;These days my usual tech stack makes use of &lt;code class=&quot;language-text&quot;&gt;androidx.lifecycle&lt;/code&gt; classes with Kotlin extensions and coroutines. Setting up unit tests for all this just takes a couple of JUnit rules!&lt;/p&gt;
&lt;p&gt;Let&apos;s start by imagining a fantasy app where you can buy cyber implants.&lt;/p&gt;
&lt;h2&gt;Start small&lt;/h2&gt;
&lt;p&gt;This is a view model for a listings screen that shows all available implants:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;kotlin&quot;&gt;&lt;pre class=&quot;language-kotlin&quot;&gt;&lt;code class=&quot;language-kotlin&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; Api &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getCyberImplants&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;block&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;List&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;CyberImplant&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;CyberImplantListViewModel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; api&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Api&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;ViewModel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

	&lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; MutableLiveData&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;List&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;CyberImplant&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

	&lt;span class=&quot;token keyword&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;loadData&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		api&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getCyberImplants&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; list &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;
			&lt;span class=&quot;token keyword&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; list
		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Every time &lt;code class=&quot;language-text&quot;&gt;loadData()&lt;/code&gt; is called we load some data from a backend and we update the live data object in the callback. Here is the unit test for it:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;kotlin&quot;&gt;&lt;pre class=&quot;language-kotlin&quot;&gt;&lt;code class=&quot;language-kotlin&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; CyberImplantListViewModelTests &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

	&lt;span class=&quot;token annotation builtin&quot;&gt;@get:Rule&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; instantTaskRule &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;InstantTaskExecutorRule&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

	&lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; api &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;FakeApi&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// FakeApi with canned responses&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; viewModel &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;CyberImplantListViewModel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;api&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

	&lt;span class=&quot;token annotation builtin&quot;&gt;@Test&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;`when loadData() is called, should load data`&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		viewModel&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;loadData&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;token function&quot;&gt;assertThat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;viewModel&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;`is`&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;api&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;mockImplantsList&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;We use &lt;code class=&quot;language-text&quot;&gt;InstantTaskExecutorRule&lt;/code&gt; so we can make sure our &lt;code class=&quot;language-text&quot;&gt;LiveData&lt;/code&gt; objects don&apos;t call the android main thread. Our view model logic is simple, the test mirrors that: we just need to verify if the live data value property matches what we expect.&lt;/p&gt;
&lt;h2&gt;Start small + Coroutines&lt;/h2&gt;
&lt;p&gt;Let me refactor &lt;code class=&quot;language-text&quot;&gt;CyberImplantListViewModel&lt;/code&gt;, to make use of the lovely Kotlin coroutines (with &lt;code class=&quot;language-text&quot;&gt;androidx.lifecycle:lifecycle-viewmodel-ktx&lt;/code&gt; extensions):&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;kotlin&quot;&gt;&lt;pre class=&quot;language-kotlin&quot;&gt;&lt;code class=&quot;language-kotlin&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Coroutines ❤️&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; Api &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;suspend&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getCyberImplants&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; List&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;CyberImplant&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;CyberImplantListViewModel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; api&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Api&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;ViewModel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

	&lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; MutableLiveData&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;List&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;CyberImplant&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

	&lt;span class=&quot;token keyword&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;loadData&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		viewModelScope&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;launch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;token keyword&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; api&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getCyberImplants&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The tests will remain the same, except you now need to add a rule to change the coroutines dispatcher to &lt;code class=&quot;language-text&quot;&gt;TestDispatcher&lt;/code&gt;. You can use &lt;a href=&quot;https://luisramos.dev/love-thy-viewmodel&quot;&gt;this rule&lt;/a&gt;! We don&apos;t control the &lt;code class=&quot;language-text&quot;&gt;ViewModel.viewModelScope&lt;/code&gt; extension, so the rule is the only way to make sure we can set up the correct dispatcher to run the coroutines.&lt;/p&gt;
&lt;p&gt;In this case, the &lt;code class=&quot;language-text&quot;&gt;Api&lt;/code&gt; call is responsible for changing the context to the correct one for a background task.&lt;/p&gt;
&lt;div class=&quot;blockquote info&quot;&gt;
Always make it so that every suspend method is safe to call on the main thread, since it simplifies the code a lot.
&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;kotlin&quot;&gt;&lt;pre class=&quot;language-kotlin&quot;&gt;&lt;code class=&quot;language-kotlin&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; CyberImplantListViewModelTests &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

	&lt;span class=&quot;token annotation builtin&quot;&gt;@get:Rule&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; instantTaskRule &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;InstantTaskExecutorRule&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

	&lt;span class=&quot;token comment&quot;&gt;// Make sure viewModelScope uses a test dispatcher&lt;/span&gt;
	&lt;span class=&quot;token annotation builtin&quot;&gt;@get:Rule&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; coroutinesDispatcherRule &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;CoroutineDispatcherRule&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

	&lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; api &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;FakeApi&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; viewModel &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;CyberImplantListViewModel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;api&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

	&lt;span class=&quot;token annotation builtin&quot;&gt;@Test&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;`when loadData() is called, should load data`&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; runBlockingTest &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		viewModel&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;loadData&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;token function&quot;&gt;assertThat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;`is`&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;api&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;mockImplantsList&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;We have a &lt;code class=&quot;language-text&quot;&gt;ViewModel&lt;/code&gt;, using &lt;code class=&quot;language-text&quot;&gt;LiveData&lt;/code&gt; and Kotlin coroutines, with unit tests to cover all of it. Nice job.&lt;/p&gt;
&lt;h2&gt;Add error handling!&lt;/h2&gt;
&lt;p&gt;Turns out this implementation only covers the happy path, so let&apos;s keep going and add some error handling. I really like Kotlin sealed classes to help us out with this.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;kotlin&quot;&gt;&lt;pre class=&quot;language-kotlin&quot;&gt;&lt;code class=&quot;language-kotlin&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Wrap our API results so we can avoid using try/catch in the ViewModel&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;sealed&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; Result&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;out&lt;/span&gt; T&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; Error&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;T&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; message&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; String&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Result&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;T&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; Success&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;T&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; T&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Result&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;T&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; Api &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;suspend&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getCyberImplants&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Result&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;List&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;CyberImplant&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;CyberImplantListViewModel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; api&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Api&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;ViewModel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

	&lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; uiState &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; MutableLiveData&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;UiState&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Loading&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

	&lt;span class=&quot;token keyword&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;loadData&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		viewModelScope&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;launch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
			uiState&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Loading
			&lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; result &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; api&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getCyberImplants&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
			uiState&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;when&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;result&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
				&lt;span class=&quot;token keyword&quot;&gt;is&lt;/span&gt; Result&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Error &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;result&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;message&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
				&lt;span class=&quot;token keyword&quot;&gt;is&lt;/span&gt; Result&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Success &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Success&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;result&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
			&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

	&lt;span class=&quot;token comment&quot;&gt;// Magical 🧙‍♀️&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;sealed&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; UiState &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;object&lt;/span&gt; Loading&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; UiState
		&lt;span class=&quot;token keyword&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; message&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; String&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; UiState
		&lt;span class=&quot;token keyword&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Success&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; implants&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;List&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;CyberImplant&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; UiState
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now our logic not only handles any errors the API throws, but can also show a progress bar to the user while the loading operation happens.&lt;/p&gt;
&lt;p&gt;Lets write some tests:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;kotlin&quot;&gt;&lt;pre class=&quot;language-kotlin&quot;&gt;&lt;code class=&quot;language-kotlin&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; CyberImplantListViewModelTests &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

	&lt;span class=&quot;token annotation builtin&quot;&gt;@get:Rule&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; instantTaskRule &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;InstantTaskExecutorRule&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

	&lt;span class=&quot;token annotation builtin&quot;&gt;@get:Rule&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; coroutinesDispatcherRule &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;CoroutineDispatcherRule&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

	&lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; api &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;FakeApi&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; viewModel &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;CyberImplantListViewModel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;api&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

	&lt;span class=&quot;token annotation builtin&quot;&gt;@Test&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;`given api success, when loadData() is called, should show implants`&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; runBlockingTest &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		api&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;prepareSuccess&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

		&lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; expected &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;listOf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
			UiState&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Loading&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
			UiState&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Success&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;api&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;mockImplantsList&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;token function&quot;&gt;assertThat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;uiStates&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;`is`&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;expected&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

	&lt;span class=&quot;token annotation builtin&quot;&gt;@Test&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;`given api error, when loadData() is called, should show error`&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; runBlockingTest &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		api&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;prepareError&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

		&lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; expected &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;listOf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
			UiState&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Loading&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
			UiState&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;test error&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;token function&quot;&gt;assertThat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;uiStates&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;`is`&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;expected&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I really like how, with data classes, you can declared an expected array of states and the assertThat() will give you a really good error message if the test fails. Really cleans up the test code.&lt;/p&gt;
&lt;h2&gt;Keep it flexible&lt;/h2&gt;
&lt;p&gt;To keep our UI flexible, we shouldn&apos;t be relying on the models coming from the Api. By adding models just for UI and mapping functions, if the underlying data changes, for example, a &lt;code class=&quot;language-text&quot;&gt;name&lt;/code&gt; property becomes two &lt;code class=&quot;language-text&quot;&gt;firstName&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;lastName&lt;/code&gt; properties, we just need to update our mapping of the data.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;kotlin&quot;&gt;&lt;pre class=&quot;language-kotlin&quot;&gt;&lt;code class=&quot;language-kotlin&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;CyberImplantListViewModel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; api&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Api&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;ViewModel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

	&lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; uiState &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; MutableLiveData&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;UiState&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Loading&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

	&lt;span class=&quot;token keyword&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;loadData&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		viewModelScope&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;launch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
			uiState&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Loading
			&lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; result &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; api&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getCyberImplants&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
			uiState&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;when&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;result&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
				&lt;span class=&quot;token keyword&quot;&gt;is&lt;/span&gt; Result&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Error &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;result&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;message&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
				&lt;span class=&quot;token keyword&quot;&gt;is&lt;/span&gt; Result&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Success &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Success&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;result&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toUiModel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
			&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

	&lt;span class=&quot;token keyword&quot;&gt;sealed&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; UiState &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;object&lt;/span&gt; Loading&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; UiState
		&lt;span class=&quot;token keyword&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; message&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; String&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; UiState
		&lt;span class=&quot;token keyword&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Success&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; implants&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;List&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;ImplantUiModel&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; UiState
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;ImplantUiModel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; id&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Int&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; title&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; String&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; value&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; String
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;fun&lt;/span&gt; CyberImplant&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toUiModel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;ImplantUiModel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
	id &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; id&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
	title &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; title&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
	value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; formattedValue
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;fun&lt;/span&gt; List&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;CyberImplant&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toUiModel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; map &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; it&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toUiModel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Looks exactly the same as before, but we future proof our &lt;code class=&quot;language-text&quot;&gt;ViewModel&lt;/code&gt; some.&lt;/p&gt;
&lt;p&gt;Now, we just need to update the tests for the mapping logic:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;kotlin&quot;&gt;&lt;pre class=&quot;language-kotlin&quot;&gt;&lt;code class=&quot;language-kotlin&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; CyberImplantListViewModelTests &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

	&lt;span class=&quot;token annotation builtin&quot;&gt;@get:Rule&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; instantTaskRule &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;InstantTaskExecutorRule&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

	&lt;span class=&quot;token annotation builtin&quot;&gt;@get:Rule&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; coroutinesDispatcherRule &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;CoroutineDispatcherRule&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

	&lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; api &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;FakeApi&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; viewModel &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;CyberImplantListViewModel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;api&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

	&lt;span class=&quot;token annotation builtin&quot;&gt;@Test&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;`given api success, when loadData() is called, should show implants`&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; runBlockingTest &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		api&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;prepareSuccess&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

		&lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; expected &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;listOf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
			UiState&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Loading&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
			UiState&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Success&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;api&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;mockImplantsList&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toUiModel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// &amp;lt;== UPDATE&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;token function&quot;&gt;assertThat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;uiStates&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;`is`&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;expected&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

	&lt;span class=&quot;token annotation builtin&quot;&gt;@Test&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;`given api error, when loadData() is called, should show error`&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; runBlockingTest &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		api&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;prepareError&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

		&lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; expected &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;listOf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
			UiState&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Loading&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
			UiState&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;test error&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;token function&quot;&gt;assertThat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;uiStates&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;`is`&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;expected&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Here is are the tests for the mapping function:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;kotlin&quot;&gt;&lt;pre class=&quot;language-kotlin&quot;&gt;&lt;code class=&quot;language-kotlin&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; ImplantUiModelTests &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

	&lt;span class=&quot;token annotation builtin&quot;&gt;@Test&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;`api model to ui model should map correctly`&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; apiModel &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;StubCyberImplant&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; expected &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;ImplantUiModel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
			id &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;test_id&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
			title &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;test_title&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
			value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;formatted_value&quot;&lt;/span&gt;&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;token function&quot;&gt;assertThat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;apiModel&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toUiModel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;`is`&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;expected&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;Make it wait&lt;/h2&gt;
&lt;p&gt;What if we only want to show the loading state if the API request takes longer than a certain amount of time? Let&apos;s say 200ms. With Kotlin coroutines, this isn&apos;t hard to implement and not hard at all to test!&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;kotlin&quot;&gt;&lt;pre class=&quot;language-kotlin&quot;&gt;&lt;code class=&quot;language-kotlin&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;loadData&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	viewModelScope&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;launch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; loadingJob &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; launch &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;token function&quot;&gt;delay&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;LOADING_SHOW_DELAY&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// delay for 200ms&lt;/span&gt;
			uiState&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Loading
		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

		&lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; result &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; api&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getCyberImplants&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
		uiState&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;when&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;result&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;token keyword&quot;&gt;is&lt;/span&gt; Result&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Error &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;result&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;message&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
			&lt;span class=&quot;token keyword&quot;&gt;is&lt;/span&gt; Result&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Success &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Success&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;result&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toUiModel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
		loadingJob&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;cancel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;When &lt;code class=&quot;language-text&quot;&gt;loadData()&lt;/code&gt; is called, instead of setting the state to Loading immediately, we launch a coroutine that will wait for the required amount of time and only after the Loading state will be updated. We save the &lt;code class=&quot;language-text&quot;&gt;Job&lt;/code&gt; the loading coroutine returns, and cancel it after the api request is done.&lt;/p&gt;
&lt;p&gt;If the API takes less than 200ms to return, &lt;code class=&quot;language-text&quot;&gt;loadingJob.cancel()&lt;/code&gt; will be called and cancel that coroutine execution. If it takes longer, then the &lt;code class=&quot;language-text&quot;&gt;delay()&lt;/code&gt; suspension expires, and the state is set to loading. Pretty cool!&lt;/p&gt;
&lt;p&gt;Now what about the tests?&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;kotlin&quot;&gt;&lt;pre class=&quot;language-kotlin&quot;&gt;&lt;code class=&quot;language-kotlin&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;FakeApi&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; requestDelay&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Int&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Api &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; mockImplantsList&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; List&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;CyberImplant&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;suspend&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getCyberImplants&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Result&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;List&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;CyberImplant&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token function&quot;&gt;delay&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;requestDelay&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; mockImplantsList
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; CyberImplantListViewModelTests &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

	&lt;span class=&quot;token comment&quot;&gt;// Let&apos;s say that the default delay is half a second&lt;/span&gt;
	&lt;span class=&quot;token comment&quot;&gt;// so the existing tests still pass&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; api &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;FakeApi&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;requestDelay &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;500&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; viewModel &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;CyberImplantListViewModel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;api&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

	&lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;

	&lt;span class=&quot;token annotation builtin&quot;&gt;@Test&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;`given a very fast api, when loadData() is called, should not post loading state`&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;  &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; runBlockingTest &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		api&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;requestDelay &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;

		viewModel&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;loadData&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

		&lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; expected &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;listOf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
			UiState&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Success&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;api&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;mockImplantsList&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toUiModel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// only sucess state&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;token function&quot;&gt;assertThat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;uiStates&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;`is`&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;expected&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;runBlockingTest&lt;/code&gt; is really doing all the heavy work. We are unit testing time passing but the tests run instantly! From the &lt;a href=&quot;https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/run-blocking-test.html&quot;&gt;documentation&lt;/a&gt; for &lt;code class=&quot;language-text&quot;&gt;runBlockingTest()&lt;/code&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This is similar to runBlocking but it will immediately progress past delays and into launch and async blocks. You can use this to write tests that execute in the presence of calls to delay without causing your test to take extra time.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;You could also use &lt;code class=&quot;language-text&quot;&gt;delay()&lt;/code&gt; to debounce user clicks, or make sure you refresh data after a certain amount of time. And all of that can be tested instantly.&lt;/p&gt;
&lt;h2&gt;TAP TAP TAP&lt;/h2&gt;
&lt;p&gt;To wrap up this list, let&apos;s implement a way for our users to select a implant and navigate to a detail screen. I tend to keep most of the logic in the ViewModel (easy to test), so lets have a &lt;code class=&quot;language-text&quot;&gt;Navigation&lt;/code&gt; interface with all the navigation calls.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;kotlin&quot;&gt;&lt;pre class=&quot;language-kotlin&quot;&gt;&lt;code class=&quot;language-kotlin&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;CyberImplantListViewModel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; api&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Api&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; navigation&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Navigation
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;ViewModel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

	&lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; uiState &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; MutableLiveData&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;UiState&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Loading&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

	&lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;

	&lt;span class=&quot;token keyword&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;onItemTapped&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;position&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Int&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; state &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; uiState&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; UiState&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Success	&lt;span class=&quot;token operator&quot;&gt;?:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// find the correct item&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; item &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getOrNull&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;position&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
		item&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; navigation&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;goToImplantDetail&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;it&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Our current adapter implementation calls &lt;code class=&quot;language-text&quot;&gt;viewModel.onItemTapped()&lt;/code&gt; when an item is clicked, passing the clicked position as an argument. Our tests look like this:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;kotlin&quot;&gt;&lt;pre class=&quot;language-kotlin&quot;&gt;&lt;code class=&quot;language-kotlin&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;FakeNavigation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; didCallGoToImplantDetail &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; goToImplantDetailId &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;goToImplantDetail&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;id&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Int&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
			didCallGoToImplantDetail &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
			goToImplantDetailId &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; id
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; CyberImplantListViewModelTests &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

	&lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; api &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;FakeApi&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;requestDelay &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;500&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; navigation &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;FakeNavigation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; viewModel &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;CyberImplantListViewModel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;api&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; navigation&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

	&lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;

	&lt;span class=&quot;token annotation builtin&quot;&gt;@Test&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;`when user taps an item, it should navigate to detail`&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; runBlockingTest &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// tapping before the data is loaded shouldn&apos;t do anything&lt;/span&gt;
		viewModel&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;onItemTapped&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;token function&quot;&gt;assertThat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;navigation&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;didCallGoToImplantDetail&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;`is`&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

		&lt;span class=&quot;token comment&quot;&gt;// load some data&lt;/span&gt;
		viewModel&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;loadData&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

		viewModel&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;onItemTapped&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;token function&quot;&gt;assertThat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;navigation&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;didCallGoToImplantDetail&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;`is`&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; expectedId &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; api&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;mockImplantsList&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;first&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id
		&lt;span class=&quot;token function&quot;&gt;assertThat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;navigation&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;goToImplantDetailId&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;`is`&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;expectedId&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;Wrapping up&lt;/h2&gt;
&lt;p&gt;We started with a pretty basic view model for showing a list of cybernetic implants to the user, and the respective test coverage. We then added coroutines, some logic to handle a loading state, and possible API errors. Then we wrapped it up with a &quot;navigate to detail&quot; example.&lt;/p&gt;
&lt;p&gt;Hope these examples where useful to you in some way. I really enjoy writing this, so if you want to see more examples or just want to chat about android, reach out!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Love thy ViewModel]]></title><description><![CDATA[Long gone are the days when android developers had to spend lots of time dealing with activity lifecycles when building their apps. Well…]]></description><link>https://luisramos.dev/love-thy-viewmodel</link><guid isPermaLink="false">https://luisramos.dev/love-thy-viewmodel</guid><pubDate>Fri, 29 Jan 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Long gone are the days when android developers had to spend lots of time dealing with activity lifecycles when building their apps. Well, maybe not so long ago! Now there are a lot more tools to make our jobs easier, like the google provided &lt;a href=&quot;https://developer.android.com/reference/androidx/lifecycle/package-summary&quot;&gt;androidx.lifecycle&lt;/a&gt; package.&lt;/p&gt;
&lt;p&gt;Let&apos;s talk about ViewModels, why they are a good tool, and how to use it properly.&lt;/p&gt;
&lt;p&gt;What makes them great is that you can take view state that used to be held by an activity (or fragment) and give it to this ViewModel. This state will now survive configuration changes without being leaked.&lt;/p&gt;
&lt;p&gt;Add LiveData to the mix and you strike gold:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;kotlin&quot;&gt;&lt;pre class=&quot;language-kotlin&quot;&gt;&lt;code class=&quot;language-kotlin&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; ProfileViewModel&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;ViewModel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

	&lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; username &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;MutableLiveData&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;John Silverhand&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

	&lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; cyberhacks &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; MutableLiveData&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;List&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;CyberHack&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; ProfileActivity&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Activity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

	&lt;span class=&quot;token keyword&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;onCreate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;savedInstanceState&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Bundle&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;onCreate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;savedInstanceState&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;token function&quot;&gt;setContentView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;R&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;layout&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;profile_activity_layout&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

 		&lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; viewModel &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;ViewModelProvider&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;UserModel&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;java&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
 		viewModel&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;username&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;observe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
 			textView&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;text &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; it
 		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
 		&lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The ViewModel is provided to us bound to the activity lifecycle. The LiveData objects will hold the latest data set. The activity will observe both of these LiveData objects when created, always getting the latest data available. Life is good!&lt;/p&gt;
&lt;p&gt;With this, we can establish a one way flow of data, from the ViewModel to the activity and its views. This makes unit testing the ViewModel very easy.&lt;/p&gt;
&lt;h2&gt;Mistakes were made&lt;/h2&gt;
&lt;p&gt;Now, here is an example of something you should &lt;strong&gt;not&lt;/strong&gt; do with ViewModels:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;kotlin&quot;&gt;&lt;pre class=&quot;language-kotlin&quot;&gt;&lt;code class=&quot;language-kotlin&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; ProfileViewModel&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;ViewModel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

	&lt;span class=&quot;token keyword&quot;&gt;lateinit&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; recyclerView&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; RecyclerView

	&lt;span class=&quot;token keyword&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;setView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;view&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; View&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		view&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;textView &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;John Silverhand&quot;&lt;/span&gt;&lt;/span&gt;
		recyclerView &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; view&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;recyclerView
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

	&lt;span class=&quot;token keyword&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;loadData&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		Repository&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;loadCyberHacks&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; hacks &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;
			recyclerView&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;adapter&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;items &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; hacks
		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;In this case, the view and its implementation details are inside the ViewModel. Surviving configuration changes now works against us, since holding a reference to a view will leak it after the activity gets rid of it.&lt;/p&gt;
&lt;p&gt;This will also complicate testing. The dependency on the android framework means we will need to write instrumentation tests, which are slow, harder to write, and not run as often as unit tests.&lt;/p&gt;
&lt;h2&gt;ViewModelProvider is King&lt;/h2&gt;
&lt;p&gt;The ViewModel class is very simple in its implementation. Take a peek &lt;a href=&quot;https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:lifecycle/lifecycle-viewmodel/src/main/java/androidx/lifecycle/ViewModel.java;l=107?q=ViewModel&amp;#x26;sq=&quot;&gt;here&lt;/a&gt;! It almost has more comments than actual code.&lt;/p&gt;
&lt;p&gt;Just because of it&apos;s simplicity, we should not be tricked into using it everywhere. The usage of ViewModels in an application implies the use of ViewModelProvider to create them.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;kotlin&quot;&gt;&lt;pre class=&quot;language-kotlin&quot;&gt;&lt;code class=&quot;language-kotlin&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; ProfileViewModel&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;ViewModel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;reloadData&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		Repository&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;loadCyberHacks&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; hacks &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;
			hacks&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;CyberHackViewModel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;hack&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;CyberHackViewModel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;hack&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; CyberHack&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;ViewModel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; title &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token expression&quot;&gt;hack&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; - &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token expression&quot;&gt;hack&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;expirationDate&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;In the previous snippet, there is no benefit in using a ViewModel for this mapping. A data class fits this situation much better.&lt;/p&gt;
&lt;p&gt;Whenever you see yourself creating a ViewModel using its constructor instead of a ViewModelProvider, stop! The only exception to this is, of course, writing unit tests.&lt;/p&gt;
&lt;h2&gt;Read the manual before driving&lt;/h2&gt;
&lt;p&gt;Always a good idea to read the documentation for a class before you start using it in your code. Pulled from the ViewModel javadoc:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Thee purpose of the ViewModel is to acquire and keep the information that is necessary for an Activity or a Fragment. The Activity or the Fragment should be able to observe changes in the ViewModel. ViewModels usually expose this information via &lt;code class=&quot;language-text&quot;&gt;LiveData&lt;/code&gt; or Android Data Binding. You can also use any &lt;code class=&quot;language-text&quot;&gt;Observer&lt;/code&gt; pattern from you favorite framework.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;ViewModel&apos;s only responsibility is to manage the data for the UI. It &lt;strong&gt;should never&lt;/strong&gt; access your view hierarchy or hold a reference back to the Activity or the Fragment.&lt;/p&gt;
&lt;hr/&gt;
&lt;p&gt;Hope this post helped! I leave you with two classes to help writing unit tests for ViewModels if you use LiveData objects. The &lt;code class=&quot;language-text&quot;&gt;InstantTaskExecutorRule&lt;/code&gt; will override the ArchTaskExecutor used by the LiveData to dispatch notifications:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;kotlin&quot;&gt;&lt;pre class=&quot;language-kotlin&quot;&gt;&lt;code class=&quot;language-kotlin&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; InstantTaskExecutorRule &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;TestWatcher&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;starting&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;description&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Description&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;starting&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;description&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        ArchTaskExecutor&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getInstance&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setDelegate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;TaskExecutor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;executeOnDiskIO&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;runnable&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Runnable&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                runnable&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;token keyword&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;postToMainThread&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;runnable&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Runnable&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                runnable&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;token keyword&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;isMainThread&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Boolean &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;finished&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;description&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Description&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;finished&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;description&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        ArchTaskExecutor&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getInstance&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setDelegate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If you use coroutines with &lt;code class=&quot;language-text&quot;&gt;viewModelScope&lt;/code&gt;, this &lt;code class=&quot;language-text&quot;&gt;CoroutineDispatcherRule&lt;/code&gt; will also be useful. It sets up the main coroutine dispatcher to be a &lt;code class=&quot;language-text&quot;&gt;TestCoroutineDispatcher&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;kotlin&quot;&gt;&lt;pre class=&quot;language-kotlin&quot;&gt;&lt;code class=&quot;language-kotlin&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;CoroutineDispatcherRule&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; testDispatcher&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; TestCoroutineDispatcher &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;TestCoroutineDispatcher&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;TestWatcher&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;starting&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;description&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Description&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;starting&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;description&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        Dispatchers&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setMain&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;testDispatcher&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;finished&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;description&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Description&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;finished&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;description&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        Dispatchers&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;resetMain&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        testDispatcher&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;cleanupTestCoroutines&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content:encoded></item><item><title><![CDATA[Publishing your first android library!]]></title><description><![CDATA[Publishing a library you develop is a journey on its own. It can be overwhelming, especially if you are targeting Android. There is a lot of…]]></description><link>https://luisramos.dev/publishing-your-first-android-library</link><guid isPermaLink="false">https://luisramos.dev/publishing-your-first-android-library</guid><pubDate>Tue, 15 Sep 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Publishing a library you develop is a journey on its own. It can be overwhelming, especially if you are targeting Android. There is a lot of documentation out here, but I could not find one that would go end to end with it. So I wrote this post!&lt;/p&gt;
&lt;p&gt;This came about because I released my first open source library: &lt;a href=&quot;https://github.com/orgmir/kroclin&quot;&gt;Kroclin&lt;/a&gt;! It is a snapshot testing library written in Kotlin, to help out writing less code in your tests. Check it out and give it a star if would like. Now, let me tell you about the journey to release it!&lt;/p&gt;
&lt;p&gt;Sonatype provides a &lt;a href=&quot;https://central.sonatype.org/pages/ossrh-guide.html&quot;&gt;guide for OSS repository hosting&lt;/a&gt; and &lt;a href=&quot;https://central.sonatype.org/pages/releasing-the-deployment.html&quot;&gt;another one for releasing your deployment&lt;/a&gt; that I read, but they could be more helpful. Everything you need to know is there, it just takes several readings and lots of googling to grok it. Here are the steps you need to go through:&lt;/p&gt;
&lt;p&gt;First, &lt;a href=&quot;https://issues.sonatype.org/&quot;&gt;create a Sonatype JIRA account&lt;/a&gt; if you don&apos;t have one already.&lt;/p&gt;
&lt;p&gt;Then &lt;a href=&quot;https://issues.sonatype.org/secure/CreateIssue.jspa?issuetype=21&amp;#x26;pid=10134&quot;&gt;create a new project issue&lt;/a&gt;. Make sure to click this link, since they require that you use the template. In the issue body, write that you want to claim the domain that matches the library &lt;code class=&quot;language-text&quot;&gt;groupId&lt;/code&gt;. In the case of my library, the &lt;code class=&quot;language-text&quot;&gt;groupId&lt;/code&gt; is &lt;code class=&quot;language-text&quot;&gt;dev.luisramos.kroclin&lt;/code&gt; and I claimed &lt;code class=&quot;language-text&quot;&gt;dev.luisramos.*&lt;/code&gt;. Here is &lt;a href=&quot;https://issues.sonatype.org/browse/OSSRH-60527&quot;&gt;my JIRA ticket&lt;/a&gt; as an example.&lt;/p&gt;
&lt;div class=&quot;blockquote info&quot;&gt;
I picked up from reading other issues that you only need to go through this process once, if you request a wildcard `groupId`. Other libraries released under the same `groupId` don&apos;t require a new project ticket to be created.
&lt;/div&gt;
&lt;p&gt;Note that you could also claim the &lt;code class=&quot;language-text&quot;&gt;groupId&lt;/code&gt; that matches your project hosting, for example, &lt;code class=&quot;language-text&quot;&gt;io.github.orgmir&lt;/code&gt; if that&apos;s where your project lives.&lt;/p&gt;
&lt;p&gt;After you have opened a new project ticket, you need to prove that you own the domain you claimed. You can either:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Add a TXT record to your DNS referencing the url to the JIRA ticket you created (fastest)&lt;/li&gt;
&lt;li&gt;Setup a redirect to your GitHub page&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I went with the first option, and since every DNS provider differs I won&apos;t go into specifics. Some googling on how to add a TXT record for your DNS provider should get you there.&lt;/p&gt;
&lt;div class=&quot;blockquote info&quot;&gt;
&lt;strong&gt;NOTE:&lt;/strong&gt; You will need to submit the full URL for the issue, so in my case the content for the TXT record was &lt;a href=&quot;https://issues.sonatype.org/browse/OSSRH-60527&quot;&gt;&lt;i&gt;https://issues.sonatype.org/browse/OSSRH-60527&lt;/i&gt;&lt;/a&gt;.
&lt;/div&gt;
&lt;p&gt;I went ahead and added a comment on the ticket with the output of &lt;code class=&quot;language-text&quot;&gt;dig -t TXT luisramos.dev&lt;/code&gt;, just to show that it was working. Don&apos;t know if it made a difference 😅&lt;/p&gt;
&lt;p&gt;After doing the proof of domain ownership, you will need to wait for Sonatype to reply to your ticket. They will let you know once the repository on their end is ready to receive artifacts. Don&apos;t actually upload anything until they give you the OK, your artifacts will just end up in a generic catch-all repository.&lt;/p&gt;
&lt;p&gt;After they reply, go ahead and upload your first artifact. You can copy my &lt;a href=&quot;https://github.com/Orgmir/kroclin/blob/main/gradle/gradle-mvn-push.gradle&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;gradle-mvn-push.gradle&lt;/code&gt;&lt;/a&gt; script and run &lt;code class=&quot;language-text&quot;&gt;./gradlew clean build uploadArchives&lt;/code&gt; (don&apos;t worry, I also copied it from &lt;a href=&quot;https://github.com/JakeWharton/wormhole/blob/master/gradle/gradle-mvn-push.gradle&quot;&gt;someone else&lt;/a&gt;). You could also use a plugin like &lt;a href=&quot;https://github.com/vanniktech/gradle-maven-publish-plugin&quot;&gt;vanniktech/gradle-maven-publish-plugin&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Once the upload is done, go ahead and log in to &lt;a href=&quot;https://oss.sonatype.org&quot;&gt;https://oss.sonatype.org&lt;/a&gt; using your Sonatype JIRA account.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 680px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 71.17647058823529%; position: relative; bottom: 0; left: 0; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Nexus Repository Manager&quot;
        title=&quot;Nexus Repository Manager&quot;
        src=&quot;/static/dba9d6501dcc2a8f73b6317db94c0456/c5bb3/nexus_repository_manager.png&quot;
        srcset=&quot;/static/dba9d6501dcc2a8f73b6317db94c0456/04472/nexus_repository_manager.png 170w,
/static/dba9d6501dcc2a8f73b6317db94c0456/9f933/nexus_repository_manager.png 340w,
/static/dba9d6501dcc2a8f73b6317db94c0456/c5bb3/nexus_repository_manager.png 680w,
/static/dba9d6501dcc2a8f73b6317db94c0456/eafa0/nexus_repository_manager.png 939w&quot;
        sizes=&quot;(max-width: 680px) 100vw, 680px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Select &lt;code class=&quot;language-text&quot;&gt;Staging Repositories&lt;/code&gt; on the side bar, and you should see a list with the repository for your library. Select the repository, and press &lt;code class=&quot;language-text&quot;&gt;Close&lt;/code&gt; on the top bar. This will perform some validation on Sonatype&apos;s side, and once the status updates and the repository is tagged as closed, select the repository again and press &lt;code class=&quot;language-text&quot;&gt;Release&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;If you use a plugin, the upload step will probably try to close and release the staging repository. Logging in to Nexus Repository Manager should only be needed when you are releasing for the first time, or if you use a script like me.&lt;/p&gt;
&lt;p&gt;You can now update on your JIRA ticket that the repository has been released. This will let Sonatype know, and they will trigger a sync with maven central, so your library can show when searching in &lt;a href=&quot;https://search.maven.org&quot;&gt;search.maven.org&lt;/a&gt;. This step should is also only needed the first time you are releasing.&lt;/p&gt;
&lt;p&gt;And that&apos;s it! It might take a couple of hours for the sync to happen, but congratulations! You have released a library onto the world! What a trip it was 🚀&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Ler - An RSS Aggregator for Android!]]></title><description><![CDATA[I have been working on a RSS aggregator app called Ler. Its available for Android, and you should give it a try! It is the first app I…]]></description><link>https://luisramos.dev/ler-rss-aggregator-for-android</link><guid isPermaLink="false">https://luisramos.dev/ler-rss-aggregator-for-android</guid><pubDate>Fri, 20 Mar 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I have been working on a RSS aggregator app called &lt;em&gt;Ler&lt;/em&gt;. Its available for Android, and you should &lt;a href=&quot;https://play.google.com/store/apps/details?id=app.luisramos.ler&quot;&gt;give it a try!&lt;/a&gt; It is the first app I design, develop and publish all by myself, so I am pretty proud of that!&lt;/p&gt;
&lt;p&gt;&lt;a class=&quot;text-center w-48 block mx-auto&quot; href=&apos;https://play.google.com/store/apps/details?id=app.luisramos.ler&apos;&gt;&lt;img alt=&quot;Get it on Google Play&quot; src=&quot;https://play.google.com/intl/en_us/badges/static/images/badges/en_badge_web_generic.png&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 680px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/a94c173f0a9cdbffc857cb1ed1d92a49/fa52e/ler-screenshots.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 69.41176470588235%; position: relative; bottom: 0; left: 0; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;ler app screenshots&quot;
        title=&quot;ler app screenshots&quot;
        src=&quot;/static/a94c173f0a9cdbffc857cb1ed1d92a49/c5bb3/ler-screenshots.png&quot;
        srcset=&quot;/static/a94c173f0a9cdbffc857cb1ed1d92a49/04472/ler-screenshots.png 170w,
/static/a94c173f0a9cdbffc857cb1ed1d92a49/9f933/ler-screenshots.png 340w,
/static/a94c173f0a9cdbffc857cb1ed1d92a49/c5bb3/ler-screenshots.png 680w,
/static/a94c173f0a9cdbffc857cb1ed1d92a49/b12f7/ler-screenshots.png 1020w,
/static/a94c173f0a9cdbffc857cb1ed1d92a49/b5a09/ler-screenshots.png 1360w,
/static/a94c173f0a9cdbffc857cb1ed1d92a49/fa52e/ler-screenshots.png 3288w&quot;
        sizes=&quot;(max-width: 680px) 100vw, 680px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;I know what you are thinking, &lt;em&gt;another&lt;/em&gt; RSS feed app? Aren&apos;t there billions of them? Well, yes. But this one is mine. And I have plans. Big plans! Let me tell you how it came to be, and what I want to do with it.&lt;/p&gt;
&lt;h3&gt;Twitter is a time sink&lt;/h3&gt;
&lt;p&gt;I spend too much time on twitter. I checked my phone usage settings for apps, and twitter is always number one on the list, toppling YouTube (!!) and WhatsApp. I though I watched a lot of YouTube, but apparently, on a very boring day, I accumulated &lt;em&gt;4 hours&lt;/em&gt; of twitter usage...&lt;/p&gt;
&lt;p&gt;So I looked into Googles digital well-being app, and turned on a 30 minute timeout for Twitter. And it works! Some days, I don&apos;t even reach the timeout. Unfortunately, this also meant that a lot of the blogs I read wouldn&apos;t reach me as well.&lt;/p&gt;
&lt;p&gt;And there you have it. I took the time I was wasting and built this app!&lt;/p&gt;
&lt;h3&gt;Ler&lt;/h3&gt;
&lt;p&gt;The app name is the portuguese word for &lt;em&gt;reading&lt;/em&gt;. I did a search and it seems nobody claimed it, so why not? The main features that I have built so far:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You can subscribe to RSS feeds (crazy I know). You can manually type in a website address, or you can share from the browser to the app!&lt;/li&gt;
&lt;li&gt;You can filter out articles that you already read, or just show articles from one feed.&lt;/li&gt;
&lt;li&gt;You can swipe an article to toggle it read, or mark all articles in a feed at once!&lt;/li&gt;
&lt;li&gt;Feeds will be checked for updates every hour auto-magically.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Articles currently open in the browser. I toyed with the idea of having a built in reader, but that seemed overkill for what I wanted to do. I already used the browser to do most of my reading anyway.&lt;/p&gt;
&lt;p&gt;I am pretty proud of how it turned out, despite not being great at UI design, I kept it simple and it does the job! I have been using it for the past couple of weeks, and I think I impressed myself by actually using it instead of defaulting to Twitter as a time killer.&lt;/p&gt;
&lt;h3&gt;Open so everyone can &lt;em&gt;read&lt;/em&gt; it&lt;/h3&gt;
&lt;p&gt;Not only am I using this app to avoid using twitter all the time, I will also use it as a showcase project. Hence, my plan in the next weeks is to write a bunch of tests, clean up the folder structure, and open source it.&lt;/p&gt;
&lt;p&gt;So far, I have not worked in an app that had their source open, so most often when talking about my work I can&apos;t provide examples. I applied all my knowledge and tried to build the simplest app I could, while still being a good example of how I would build an app.&lt;/p&gt;
&lt;p&gt;My plan is to also write about android development, while using the app as an example. I made some architecture choices that I can talk about, since I haven&apos;t found a lot of written examples online.&lt;/p&gt;
&lt;p&gt;Please give it a spin, and let me know what you think! You can &lt;a href=&quot;https://twitter.com/luisramos1337&quot;&gt;tweet at me&lt;/a&gt; or send me an [email](mailto:&lt;a href=&quot;mailto:luis.ramos@hey.com&quot;&gt;luis.ramos@hey.com&lt;/a&gt;?subject=Ler Feedback). I have a little roadmap with more features, but let me know if you want a particular feature added, and I will add it to the list!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[My new blog! - Creating a Gatsby website]]></title><description><![CDATA[Welcome to my new blog! Hope you enjoy it, I have spent the last couple of weeks building it. Now that it is done, I can finally start…]]></description><link>https://luisramos.dev/new-blog-creating-gatsby-website</link><guid isPermaLink="false">https://luisramos.dev/new-blog-creating-gatsby-website</guid><pubDate>Tue, 17 Mar 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Welcome to my new blog! Hope you enjoy it, I have spent the last couple of weeks building it. Now that it is done, I can finally start writing about stuff!&lt;/p&gt;
&lt;p&gt;&lt;em&gt;I kinda rebuilt it because I wanted to write about building it...&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;So let me tell you how it works, what I learned, and how easy it is to do it!&lt;/p&gt;
&lt;h2&gt;Tech stack&lt;/h2&gt;
&lt;p&gt;To be upfront about it, this blog is built using &lt;a href=&quot;https://www.gatsbyjs.org/&quot;&gt;Gatsby&lt;/a&gt;, and deployed using GitHub Actions to a Digital Ocean droplet. And I am very happy with it. This entire website is open source, so if you wanna check the code &lt;a href=&quot;https://github.com/orgmir/luisramos.dev&quot;&gt;go ahead&lt;/a&gt;!&lt;/p&gt;
&lt;p&gt;One day I was reading some blog posts from Tania Rascia and &lt;a href=&quot;https://www.taniarascia.com/migrating-from-wordpress-to-gatsby/&quot;&gt;this post&lt;/a&gt; made me look into Gatsby. In it she migrated her WordPress blog to a Gatsby website that she built from a pretty bare bones template. I have been thinking of revamping my blog again, since the last one was &lt;a href=&quot;/migration-to-hugo/&quot;&gt;more than a year ago&lt;/a&gt;. Pairing that with my interest with this new JAMStack thing, I just got excited and started looking through the docs!&lt;/p&gt;
&lt;p&gt;After building this website, I really enjoyed working with the plugin system and I loved that it used React and Graphql.&lt;/p&gt;
&lt;h3&gt;Getting started&lt;/h3&gt;
&lt;p&gt;Following Tania&apos;s footsteps, I installed the &lt;code class=&quot;language-text&quot;&gt;gatsby-cli&lt;/code&gt; and created a website using the &lt;a href=&quot;https://github.com/vagr9k/gatsby-advanced-starter/&quot;&gt;gatsby-advanced-starter&lt;/a&gt;. Turns out, that was totally overkill for me, and most of the things there overwhelmed me. So I tried again running &lt;code class=&quot;language-text&quot;&gt;gatsby new luisramos.dev&lt;/code&gt; and slowly added the plugins I needed as I went along.&lt;/p&gt;
&lt;p&gt;Starting with the empty project, the first thing I did was setup &lt;a href=&quot;tailwindcss.com/&quot;&gt;Tailwind CSS&lt;/a&gt;. I am not very good in terms of design skills, and playing around with CSS is fun but can also become a huge waste of time. Tailwind fits nicely into the &quot;just make a component&quot; React world, and paired with a fantastic documentation page, it is very easy to work with. You can quickly create your styles using the existing classes, and when it you start duplicating a lot, you move everything to a component! 👍&lt;/p&gt;
&lt;p&gt;Setting up Tailwind with Gatsby was as easy as adding the plugin &lt;code class=&quot;language-text&quot;&gt;gatsby-plugin-postcss&lt;/code&gt; to gatsby configuration, adding a &lt;code class=&quot;language-text&quot;&gt;postcss.config.js&lt;/code&gt; requiring the Tailwind library, and adding the Tailwind css imports to the &lt;code class=&quot;language-text&quot;&gt;global.css&lt;/code&gt; file.&lt;/p&gt;
&lt;p&gt;I am very impressed with the plugin system. Anything you need, and you bet there is a plugin that does it! I wanted a RSS feed with all my posts, there&apos;s a plugin for that: &lt;code class=&quot;language-text&quot;&gt;gatsby-plugin-feed&lt;/code&gt;. I want my posts to be in markdown, there&apos;s a plugin for that: &lt;code class=&quot;language-text&quot;&gt;gatsby-transformer-remark&lt;/code&gt;. I wanted syntax highlighting for my code snippets, there&apos;s even a plugin for the plugin: &lt;code class=&quot;language-text&quot;&gt;gatsby-remark-prismjs&lt;/code&gt;! Overall, very wonderful to work with.&lt;/p&gt;
&lt;p&gt;This plugin system even got me wanting to build more websites!&lt;/p&gt;
&lt;h3&gt;React+Graphql&lt;/h3&gt;
&lt;p&gt;Another feature I enjoyed was the React and Graphql support. In the beginning it felt very overkill to have a graphql query everywhere, but the more I read the documentation, the more it made sense to me.&lt;/p&gt;
&lt;p&gt;I have enough experience with React that I could jump straight into this new website building my layout. After I had build the header/footer/nav combo, I started thinking about the main page, and I decided that I wanted the latest three posts there. So when you land, you can immediately read some stuff.&lt;/p&gt;
&lt;p&gt;To do this, you write a graphql query in your React page, name it &lt;code class=&quot;language-text&quot;&gt;pageQuery&lt;/code&gt; and export it. Then, Gatsby will take the data generated from that query and feed it to the component.&lt;/p&gt;
&lt;p&gt;The whole experience around this was incredible: I played around with the graphql browser until I had the data I wanted, I pasted the query into the page component, and like magic the data was there! Graphql is pretty sweet.&lt;/p&gt;
&lt;p&gt;After that, I added some code to &lt;code class=&quot;language-text&quot;&gt;gatsby-node.js&lt;/code&gt; so it would create a page for all of my posts. It uses a template, so I don&apos;t need to create a component for each post manually. After that, I added a contact and about pages, an RSS feed, some more styling and a bit of content. Sooner than I was expecting, I had a website ready to be deployed!&lt;/p&gt;
&lt;h2&gt;Deployment via GitHub Actions&lt;/h2&gt;
&lt;p&gt;My previous blog was hosted on the same &lt;a href=&quot;%5Bhttps://www.digitalocean.com%5D(https://www.digitalocean.com/)&quot;&gt;Digital Ocean&lt;/a&gt; droplet that this one is. I have this one box that I use to play around with several things, so since I am already paying for it, and I don&apos;t drive a lot of traffic, I planned the same for this blog.&lt;/p&gt;
&lt;p&gt;The deployment steps to get this into the server are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Run &lt;code class=&quot;language-text&quot;&gt;gatsby build&lt;/code&gt; to get the static version of the website&lt;/li&gt;
&lt;li&gt;Upload the generated folder to the server, preferably using &lt;code class=&quot;language-text&quot;&gt;rsync&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So I shopped around a bit, and I am impressed by the amount of GitHub actions already available. I created a workflow that checkouts my code, runs the build and uploads the website to my server in less than half an hour. And it caches the &lt;code class=&quot;language-text&quot;&gt;~/.npm&lt;/code&gt; folder, so it all runs in 2 mins on CI.&lt;/p&gt;
&lt;h2&gt;Yay Gatsby&lt;/h2&gt;
&lt;p&gt;I am very happy with the current setup. This website already has pulled this post out of me, so lets see if it makes me write more! Hope you enjoyed this write up as much as I have enjoyed building the site.&lt;/p&gt;
&lt;p&gt;If you liked this post, or if you just wanna send me some feedback, send me a &lt;a href=&quot;https://twitter.com/luisramos1337&quot;&gt;tweet&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;As a final note, I wrote this post in a very cool markdown editor, &lt;a href=&quot;https://typora.io/&quot;&gt;Typora&lt;/a&gt;. It is free during beta, so give it a try!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Oh My Zsh prompt theme for Windows Powershell!]]></title><description><![CDATA[I do most of my programming in my mac these days, but once in a while my gaming desktop becomes the testground for some code bashing. Since…]]></description><link>https://luisramos.dev/windows-powershell-oh-my-zsh</link><guid isPermaLink="false">https://luisramos.dev/windows-powershell-oh-my-zsh</guid><pubDate>Sat, 04 May 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I do most of my programming in my mac these days, but once in a while my gaming desktop becomes the testground for some code bashing. Since I use git on the terminal, on windows I use powershell instead of a sweet zsh shell.&lt;/p&gt;
&lt;p&gt;Working with powershell is not that bad, but I terribly miss not having the git branches on my prompt. Since I really enjoy the &lt;a href=&quot;https://ohmyz.sh/&quot;&gt;Oh My Zsh&lt;/a&gt; theme on my Mac, here’s a snippet that changes your power shell prompt!&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;powershell&quot;&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; prompt &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
\&lt;span class=&quot;token variable&quot;&gt;$ESC&lt;/span&gt; = &lt;span class=&quot;token namespace&quot;&gt;[char]&lt;/span&gt;27

&lt;span class=&quot;token variable&quot;&gt;$p&lt;/span&gt; = &lt;span class=&quot;token function&quot;&gt;Split-Path&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;leaf &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;path &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Get-Location&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token variable&quot;&gt;$branch&lt;/span&gt; = $&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;git symbolic-ref &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;q HEAD&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-replace&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;refs/heads/&quot;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$branch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$branch&lt;/span&gt; = &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$ESC&lt;/span&gt;[34mgit:(&lt;span class=&quot;token variable&quot;&gt;$ESC&lt;/span&gt;[0m&lt;span class=&quot;token variable&quot;&gt;$ESC&lt;/span&gt;[31m&lt;span class=&quot;token variable&quot;&gt;$branch&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$ESC&lt;/span&gt;[0m&lt;span class=&quot;token variable&quot;&gt;$ESC&lt;/span&gt;[34m)&lt;span class=&quot;token variable&quot;&gt;$ESC&lt;/span&gt;[0m &quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$ESC&lt;/span&gt;[1m&lt;span class=&quot;token variable&quot;&gt;$ESC&lt;/span&gt;[32m&lt;span class=&quot;token function&quot;&gt;$&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;[char]&lt;/span&gt;0x279C&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$ESC&lt;/span&gt;[0m &lt;span class=&quot;token variable&quot;&gt;$ESC&lt;/span&gt;[36m&lt;span class=&quot;token variable&quot;&gt;$p&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$ESC&lt;/span&gt;[0m &lt;span class=&quot;token variable&quot;&gt;$branch&lt;/span&gt;\&lt;span class=&quot;token variable&quot;&gt;$ESC&lt;/span&gt;[0m&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This code lands in the &lt;code class=&quot;language-text&quot;&gt;$profile&lt;/code&gt; file, which probably needs to be created:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;powershell&quot;&gt;&lt;pre class=&quot;language-powershell&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;new-item&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;itemtype file &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;path &lt;span class=&quot;token variable&quot;&gt;$profile&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;force
notepad &lt;span class=&quot;token variable&quot;&gt;$PROFILE&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Notepad should open up and you can copy paste the snipped there!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Auto Layout UIView Extension: A quick way to programatically create layouts]]></title><description><![CDATA[Recently I have taken a renewed interest into this blog. I have revamped the theme (mostly stealing it from my friend's blog, check it out…]]></description><link>https://luisramos.dev/autolayout-uiview-extension</link><guid isPermaLink="false">https://luisramos.dev/autolayout-uiview-extension</guid><pubDate>Tue, 30 Apr 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Recently I have taken a renewed interest into this blog. I have revamped the theme (mostly stealing it from my friend&apos;s blog, &lt;a href=&quot;https://accidental.dev/&quot;&gt;check it out&lt;/a&gt;) and now I&apos;m focusing on writing more regularly. I noticed that a lot of my code snippets are just sitting quietly on GitHub or Gitlab and could use a post or two to describe why I made them!&lt;/p&gt;
&lt;p&gt;The first one I want to talk about is a UIView extension that I always use in my iOS projects. I always prefer to build my interface programmatically, so that lead me to search for a few auto layout libraries.&lt;/p&gt;
&lt;p&gt;The issue I have with using an auto layout library is that if a new developer joins the team, that person will need to deal with my choice of library that usually comes with its own set of idioms and abstractions. My experience is that people enjoy building layouts differently and the preference will always be for some other library! Also, the added abstractions leave me a bit too far away from the auto layout specifics.&lt;/p&gt;
&lt;p&gt;Instead of choosing a library, a few years ago I created an extension with just a couple of methods to see if I could get away without adding a dependency.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;swift&quot;&gt;&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;align&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;with view&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;UIView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; constant&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;CGFloat&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  translatesAutoresizingMaskIntoConstraints &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;
  leftAnchor&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;constraint&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;equalTo&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; view&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;leftAnchor&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; constant&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; constant&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isActive &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
  rightAnchor&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;constraint&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;equalTo&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; view&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;rightAnchor&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; constant&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;constant&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isActive &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
  topAnchor&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;constraint&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;equalTo&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; view&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;topAnchor&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; constant&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; constant&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isActive &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
  bottomAnchor&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;constraint&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;equalTo&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; view&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;bottomAnchor&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; constant&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;constant&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isActive &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I kept adding to it as I needed and it kept growing. Currently it looks more like this:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;swift&quot;&gt;&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token attribute atrule&quot;&gt;@discardableResult&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;align&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;with view&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UIView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; constant&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CGFloat&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;NSLayoutConstraint&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  translatesAutoresizingMaskIntoConstraints &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; constraints &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
    leftAnchor&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;constraint&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;equalTo&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; view&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;leftAnchor&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; constant&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; constant&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    rightAnchor&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;constraint&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;equalTo&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; view&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;rightAnchor&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; constant&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;constant&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    topAnchor&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;constraint&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;equalTo&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; view&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;topAnchor&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; constant&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; constant&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    bottomAnchor&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;constraint&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;equalTo&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; view&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;bottomAnchor&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; constant&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;constant&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;token class-name&quot;&gt;NSLayoutConstraint&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;activate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;constraints&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; constraints
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token attribute atrule&quot;&gt;@discardableResult&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;alignTop&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;to anchor&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NSLayoutYAxisAnchor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; constant&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CGFloat&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; priority&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UILayoutPriority&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;required&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; isActive&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Bool&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NSLayoutConstraint&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  translatesAutoresizingMaskIntoConstraints &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; constraint &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; topAnchor&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;constraint&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;equalTo&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; anchor&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; constant&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; constant&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  constraint&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;priority &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; priority
  constraint&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isActive &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; isActive
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; constraint
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;As you can see, some wrapper functions with sensible defaults make auto layout better to work with. The code becomes cleaner and easier to read, and you don&apos;t loose any of the features that auto layout gives us.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;swift&quot;&gt;&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;viewDidLoad&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;viewDidLoad&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// ... setup view code&lt;/span&gt;

  containerView&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;align&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;with&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; view&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  textView&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;alignTop&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;with&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; view&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; constant&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;40&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  textView&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;alignLeadingTrailing&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;with&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; view&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; constant&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This extension has grown after several years of copying this over to each new project and adding something that was missing. It fits my needs well, so I have stopped looking for an auto layout library! The full extension is available &lt;a href=&quot;https://gist.github.com/Orgmir/a140b15c1f2ab86b2a72d4c09570cd52&quot;&gt;in this public gist.&lt;/a&gt; Thanks for reading!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Blog revamp: Migrating to Hugo]]></title><description><![CDATA[Update: Since this post was made, I have moved my blog to a new website and tech stack. Check out the post about the new revamp! The blog…]]></description><link>https://luisramos.dev/migration-to-hugo</link><guid isPermaLink="false">https://luisramos.dev/migration-to-hugo</guid><pubDate>Thu, 27 Sep 2018 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;em&gt;&lt;strong&gt;Update:&lt;/strong&gt;&lt;/em&gt; Since this post was made, I have moved my blog to a new website and tech stack. Check out the post about the new &lt;a href=&quot;/new-blog-creating-gatsby-website&quot;&gt;revamp&lt;/a&gt;!&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;The blog has a new look! I like to think it looks a bit better now. Of course the fun side of it is the tech stack. It is now generated using &lt;a href=&quot;http://gohugo.io/&quot;&gt;hugo&lt;/a&gt;, deployed using &lt;a href=&quot;http://gitlab.com/&quot;&gt;Gitlab&lt;/a&gt; incredible CI pipelines, and also I&apos;m hosting it on a &lt;a href=&quot;https://www.digitalocean.com/&quot;&gt;Digital Ocean&lt;/a&gt; droplet.&lt;/p&gt;
&lt;p&gt;I want to talk about my experience migrating from Jekyll to hugo, and how I went about setting everything up.&lt;/p&gt;
&lt;h2&gt;Migrating to Hugo&lt;/h2&gt;
&lt;p&gt;This blog was previously hosted in Github pages, and it used Jekyll to render the static pages. All the content was in markdown already, so it was relatively easy to migrate it to hugo. The content structure is relatively similar in both frameworks. I only had trouble with the syntax highlight for my code snippets, and with processing the template sass files to CSS.&lt;/p&gt;
&lt;p&gt;Hugo uses a code syntax generator called &lt;a href=&quot;https://github.com/alecthomas/chroma&quot;&gt;Chroma&lt;/a&gt;. The only change is that instead of using the normal markdown code marks, it uses specific tags:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;go&quot;&gt;&lt;pre class=&quot;language-go&quot;&gt;&lt;code class=&quot;language-go&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; highlight swift &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You also need to declare the language you are highlighting (&lt;code class=&quot;language-text&quot;&gt;swift&lt;/code&gt; in that example).&lt;/p&gt;
&lt;p&gt;Generating new content is super easy and fast, you just run the following command:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;hugo new posts/title-of-blog-post.md&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This command will create a new file in the specified content folder, with some front-matter meta data already setup for you to fill.&lt;/p&gt;
&lt;p&gt;The template system is also straightforward to use, and you can easily define new content types and front-matter, to be used by the &lt;code class=&quot;language-text&quot;&gt;hugo new&lt;/code&gt; command.&lt;/p&gt;
&lt;h3&gt;Hugo Extended&lt;/h3&gt;
&lt;p&gt;I found a gap in documentation about this, but it seems that hugo has an &quot;extended&quot; version. It adds extras, like sass processing, to the usual binary.&lt;/p&gt;
&lt;p&gt;When I tried to edit the template sass files, the changes weren&apos;t visible on refresh. They were being ignored, and since I was running locally there was no change it could be some cache issue.
I eventually figured out that the template was using generated resources in a &lt;code class=&quot;language-text&quot;&gt;resources/_gen/&lt;/code&gt; folder, and deleting that basically broke the generation. Since no CSS was found, hugo threw an error and didn&apos;t generate the website.&lt;/p&gt;
&lt;p&gt;After googling around, I found out about the extended version of hugo that you can download from their releases page. Running the extended version compiled the sass files into CSS, and made everything work again. But now this means I need the extended version to generate my website, since I want to process the sass files on every generation.&lt;/p&gt;
&lt;h2&gt;Deployment with Gitlab CI&lt;/h2&gt;
&lt;p&gt;If you haven&apos;t tried &lt;a href=&quot;http://gitlab.com/&quot;&gt;Gitlab&lt;/a&gt; you should really give it a spin. I&apos;ll to not fanboy about it too much, but they have some amazing features.&lt;/p&gt;
&lt;p&gt;One of them is the ability to setup a CI pipeline, writing some configuration on a &lt;code class=&quot;language-text&quot;&gt;.gitlab-ci.yml&lt;/code&gt; at the root of your repo. This way, every time I push a commit to master, a deployment is triggered by Gitlab.
You get 1000 free minutes of running time each month, more than enough for all your blogging needs.&lt;/p&gt;
&lt;p&gt;Since I have to use hugo extended to generate my website, I couldn&apos;t use the suggested image to run my build. I created a new &lt;code class=&quot;language-text&quot;&gt;Dockerfile&lt;/code&gt; that installs the right hugo binary, and also all the commands needed for deployment. This highlights another cool feature of Gitlab: you have a container register for each project, meaning you can have your container images right next to the project they are relevant to.&lt;/p&gt;
&lt;p&gt;After I built and pushed the image, I just needed to reference it in my CI file to get it running the deployment without a hitch! It&apos;s pretty satisfying to finally get a CI job green. Check out the &lt;a href=&quot;https://gitlab.com/snippets/1757791&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;.gitlab-ci.yml&lt;/code&gt;&lt;/a&gt; and the &lt;a href=&quot;https://gitlab.com/snippets/1757792&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;Dockerfile&lt;/code&gt;&lt;/a&gt; used for this.&lt;/p&gt;
&lt;h2&gt;Hosting on Digital Ocean&lt;/h2&gt;
&lt;p&gt;The final command of the deployment is a rsync into a Digital Ocean droplet. Pretty satisfied with the setup process for creating a droplet, it&apos;s a one button click for most of what you need. Haven&apos;t had an issue with it yet!&lt;/p&gt;
&lt;p&gt;I am using nginx to serve the website on the droplet, which was fairly easy to setup with all the info that is available online. I setup certbot to create and automatically renew SSL certificates for the website. It also takes care of updating the website config for you.&lt;/p&gt;
&lt;p&gt;Also, since Cloudflare has a one website free tier, I got all of my traffic running through it. So hopefully, my droplet will barely be touched!&lt;/p&gt;
&lt;h2&gt;DevOps for the common man&lt;/h2&gt;
&lt;p&gt;I am really happy with this setup. Hugo is a powerful framework, quick to setup but robust enough to handle more heavy duty websites. Gitlab enables DevOps for the common man, giving me control over it but in a way that I&apos;m not overwhelmed. And hosting solutions like Digital Ocean used in conjunction with CDNs like Cloudflare mean that you can serve a lot of people for cheap. A lot can be done by leveraging this infrastructure, which gets me excited!&lt;/p&gt;
&lt;p&gt;Next up, I&apos;ll add a static CMS like &lt;a href=&quot;https://www.netlifycms.org/&quot;&gt;Netlify&lt;/a&gt; to this blog so I can write from anywhere!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[iOS local notifications ninja bug!]]></title><description><![CDATA[Recently I was working in a project that require me to show a local notification. And for the love of XCode, the app would not show an alert…]]></description><link>https://luisramos.dev/ios-local-notification-ninja-bug</link><guid isPermaLink="false">https://luisramos.dev/ios-local-notification-ninja-bug</guid><pubDate>Tue, 26 Sep 2017 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Recently I was working in a project that require me to show a local notification. And for the love of XCode, the app would not show an alert! I was at a loss!&lt;/p&gt;
&lt;p&gt;Beginning with iOS 10, the iOS notification framework was revamped to unify both remote and local notifications. This threw me into a loop while trying to figure out the problem. The only thing I had to go by was an error message that popped up when adding the notification to UNUserNotificationCenter:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;objc&quot;&gt;&lt;pre class=&quot;language-objc&quot;&gt;&lt;code class=&quot;language-objc&quot;&gt;Adding notification request failed with error&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Error Domain&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;NSCocoaErrorDomain Code&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;4097&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;connection to service named com.apple.usernotifications.usernotificationservice&quot;&lt;/span&gt; UserInfo&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;NSDebugDescription&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;connection to service named com&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;apple&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;usernotifications&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;usernotificationservice&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;What a cryptic message, thanks Apple Dev team! I had a custom action setup, so the user could open a url with more info. I was adding this URL to the &lt;code class=&quot;language-text&quot;&gt;userInfo&lt;/code&gt; of the notification and here was where the bug lived! The issue was that I was adding the URL object, instead of an absolute string of the url. As soon as I changed this, everything worked.&lt;/p&gt;
&lt;p&gt;Of course, a &lt;a href=&quot;https://stackoverflow.com/questions/41360531/unmutablenotificationcontent-with-custom-object-in-userinfo&quot;&gt;stackoverflow answer&lt;/a&gt; helped me figure this out!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Fastlane Supply uploads everything!]]></title><description><![CDATA[Ever since I found out about , I've been using it to deploy Android and iOS apps. It's amazing how streamlined things can get: it saves you…]]></description><link>https://luisramos.dev/fastlane-supply-uploads-everything</link><guid isPermaLink="false">https://luisramos.dev/fastlane-supply-uploads-everything</guid><pubDate>Tue, 05 Sep 2017 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Ever since I found out about &lt;a href=&quot;https://fastlane.tools/&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;fastlane&lt;/code&gt;&lt;/a&gt;, I&apos;ve been using it to deploy Android and iOS apps. It&apos;s amazing how streamlined things can get: it saves you a bunch of time when deploying a release; it supports integrations with your favorite distribution platforms (yay Crashlytics); and you can easily copy the setup from one app to the other! Couldn&apos;t be happier with it.&lt;/p&gt;
&lt;p&gt;Here&apos;s a tip for the Android devs out there using &lt;a href=&quot;https://fastlane.tools/&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;fastlane&lt;/code&gt;&lt;/a&gt;, add this to your &lt;code class=&quot;language-text&quot;&gt;before_all&lt;/code&gt; step:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ruby&quot;&gt;&lt;pre class=&quot;language-ruby&quot;&gt;&lt;code class=&quot;language-ruby&quot;&gt;before &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;all &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;
  sh &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;rm -f ../app/build/outputs/apk/*/\_/\*.apk&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I&apos;ve found that when deploying Android builds using &lt;code class=&quot;language-text&quot;&gt;supply&lt;/code&gt;, the tool tries to upload whatever it can find in your &lt;code class=&quot;language-text&quot;&gt;build/outputs&lt;/code&gt; folder. And often the error you get are unrelated like:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;objc&quot;&gt;&lt;pre class=&quot;language-objc&quot;&gt;&lt;code class=&quot;language-objc&quot;&gt;Google Api Error&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; apkUpgradeVersionConflict&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
APK specifies a version code that has already been used&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This is obviously not true if you check your google play console and your build.gradle file. Looking at the output the (at the &lt;code class=&quot;language-text&quot;&gt;GRADLE_ALL_APK_OUTPUT_PATHS&lt;/code&gt; variable), it seems supply is trying to upload every apk he finds, including debugging version and other flavors. Adding that line will save you some time and stop you from screaming &quot;Why don&apos;t the versions match??&quot;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Xcode crash on auto-complete: Snippets!]]></title><description><![CDATA[My XCode crashed after updating it to version 7.2. It happened every time I tried to use auto-complete in an Obj-c file. Since my project…]]></description><link>https://luisramos.dev/xcode-crash-autocomplete-snippets</link><guid isPermaLink="false">https://luisramos.dev/xcode-crash-autocomplete-snippets</guid><pubDate>Sun, 07 Feb 2016 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;My XCode crashed after updating it to version 7.2. It happened every time I tried to use auto-complete in an Obj-c file. Since my project was in Swift, it didn&apos;t bother me much. Things got worse when I actually needed to edit some library code!&lt;/p&gt;
&lt;p&gt;It crash with this exception:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;objc&quot;&gt;&lt;pre class=&quot;language-objc&quot;&gt;&lt;code class=&quot;language-objc&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;NSInvalidArgumentException&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;__NSCFData isEqualToString&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; unrecognized selector sent to instance &lt;span class=&quot;token number&quot;&gt;0x7fd01c5d4530&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Since I got bored fast using a text editor to work around it, I tried to fix it. I use some plugins, one of them being &lt;a href=&quot;https://github.com/FuzzyAutocomplete/FuzzyAutocompletePlugin&quot;&gt;fuzzy-autocomplete&lt;/a&gt; (if you don&apos;t use it, check it out, it&apos;s awesome!) I assumed the problem was with one of them. I removed them one by one and everytime I tried, BAM! Still crashing!&lt;/p&gt;
&lt;p&gt;This was bad, because plugins where my only option. After googling for a while, I didn&apos;t find anyone with a similar problem, so I guessed it had something to do with my custom installation. And this is dumb, but I should have looked to the XCode crash log sooner...&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;objc&quot;&gt;&lt;pre class=&quot;language-objc&quot;&gt;&lt;code class=&quot;language-objc&quot;&gt;&lt;span class=&quot;token number&quot;&gt;6&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0x00000001038f4708&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;IDECodeSnippetLibraryCompletionStrategy_scope&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;matchesScope&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;atBOL&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; IDEKit&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;After checking the crash log, it was apparent who the culprit was: Snippets!&lt;/p&gt;
&lt;p&gt;It was the only custom thing left on my installation. I renamed the snippets folder, effectively stopping XCode from finding them. And guess what? Yes! Auto-complete worked again! I re-added all my loved plugins (fuzzy-autocomplete included), and back to coding I was. I didn&apos;t add my snippets, because I wasn&apos;t using them at all. But recreating them with the new XCode should solve the problem.&lt;/p&gt;
&lt;p&gt;Just another day working with XCode...&lt;/p&gt;
&lt;p&gt;PS: On a side note, if you don&apos;t use plugins with XCode, check &lt;a href=&quot;http://alcatraz.io/&quot;&gt;Alcatraz&lt;/a&gt;, a plugin manager! You are bound to find something that will improve your XCode usage :)&lt;/p&gt;</content:encoded></item><item><title><![CDATA[iOS Project Structure]]></title><description><![CDATA[To better organized my code, I have been changing my project structure along the years. I think I've finally set with one, so time to record…]]></description><link>https://luisramos.dev/ios-project-structure</link><guid isPermaLink="false">https://luisramos.dev/ios-project-structure</guid><pubDate>Mon, 11 Jan 2016 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;To better organized my code, I have been changing my project structure along the years. I think I&apos;ve finally set with one, so time to record it for the future!&lt;/p&gt;
&lt;p&gt;There are two basic guidelines when creating a project structure. First, keep it simple. Second, use something that makes sense to you.&lt;/p&gt;
&lt;p&gt;If you google a bit for this topic you may find this quora &lt;a href=&quot;https://www.quora.com/How-should-I-structure-my-iOS-app&quot;&gt;answer&lt;/a&gt; or these &lt;a href=&quot;https://github.com/futurice/ios-good-practices#project-structure&quot;&gt;guidelines&lt;/a&gt;. They both suggest something like this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;View Controllers&lt;/li&gt;
&lt;li&gt;Views&lt;/li&gt;
&lt;li&gt;View Models&lt;/li&gt;
&lt;li&gt;UI (aka storyboards)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you have small projects, this structure will work fine. But when you start adding more files to it it becomes confusing. I rather organize files in a functional way, separating them by areas and layers of the app. So it ends up something more like this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Interface
&lt;ul&gt;
&lt;li&gt;Base&lt;/li&gt;
&lt;li&gt;Onboarding&lt;/li&gt;
&lt;li&gt;Feed&lt;/li&gt;
&lt;li&gt;User&lt;/li&gt;
&lt;li&gt;Settings&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Networking&lt;/li&gt;
&lt;li&gt;Local Storage&lt;/li&gt;
&lt;li&gt;Classes
&lt;ul&gt;
&lt;li&gt;Categories/Extensions&lt;/li&gt;
&lt;li&gt;Classes that don&apos;t fit anywhere else&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Resources&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Inside those folders I usually follow the first setup, separating files by Views, ViewControllers, etc. This way you have almost every file related to an area of the app in one folder. All my images are stored in XCAssets. Fonts and other raw files are in Resources (usually with a dedicated folder for each).&lt;/p&gt;
&lt;p&gt;If you have an even awesome setup or if you just want to share yours, feel free to &lt;a href=&quot;https://twitter.com/luisramos1337&quot;&gt;send me a tweet&lt;/a&gt;.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Swift Variables in Extensions]]></title><description><![CDATA[I have recently started working on a project that uses Swift2.0! I have been using Obj-C for the past 3 years, and when Swift came I wasn't…]]></description><link>https://luisramos.dev/swift-variables-in-extensions</link><guid isPermaLink="false">https://luisramos.dev/swift-variables-in-extensions</guid><pubDate>Mon, 19 Oct 2015 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I have recently started working on a project that uses Swift2.0! I have been using Obj-C for the past 3 years, and when Swift came I wasn&apos;t one of the early adopters. Problems with the language and with SourceKit always crashing in XCode 6 didn&apos;t make for a good dev environment. Now with the 2.0 released to the wild, the promise of open source and XCode 7, Swift is good to go!&lt;/p&gt;
&lt;p&gt;One of the first problems I had is one that happens to many iOS developers that use extensions/categories in their projects: How do I add a variable (static or not) to an extension?&lt;/p&gt;
&lt;p&gt;Since I seem to always be googling for this one, I&apos;ll log it here for future convenience.&lt;/p&gt;
&lt;h3&gt;Static Variables&lt;/h3&gt;
&lt;p&gt;For a static variable, there is actually a really neat way:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;swift&quot;&gt;&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;extension&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UIView&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Static&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; defaultPadding &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CGFloat&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;16&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;widthWithPadding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CGFloat&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;bounds&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;width &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Static&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;defaultPadding
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Since you can declare structs in an extension, you can use these value objects to hold your static variables and they function the same way as declaring a static variable on the class. If you don&apos;t like the extra &lt;code class=&quot;language-text&quot;&gt;Static.&lt;/code&gt; declaration, you can create wrappers using computed variables:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;swift&quot;&gt;&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;extension&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UIView&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; defaultPadding &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CGFloat&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Static&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;defaultPadding &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;Instance Variables&lt;/h3&gt;
&lt;p&gt;For instance variables the solution is to use the obj-c runtime. You need to create computed variables that get and set an associated object using &lt;code class=&quot;language-text&quot;&gt;objc_getAssociatedObject()&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;objc_setAssociatedObject()&lt;/code&gt;. This is the same approach taken if using it with Obj-C.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;swift&quot;&gt;&lt;pre class=&quot;language-swift&quot;&gt;&lt;code class=&quot;language-swift&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ObjectiveC&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;extension&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UIViewController&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Static&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MainTitle&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-literal&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;\_MainTitle&quot;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; mainTitle&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;objc_getAssociatedObject&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Static&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;MainTitle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; newValue &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; newValue &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;objc_setAssociatedObject&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Static&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;GenericTitle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;newValue &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NSString&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;OBJC_ASSOCIATION_RETAIN_NONATOMIC&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;That way you can extend to your heart desire! If you want to understand the difference between structs and classes check out the &lt;a href=&quot;appledocs-class-vs-strutc&quot;&gt;Apple docs&lt;/a&gt;. And to find out more about what kind of magic you can do with obj-c runtime check out &lt;a href=&quot;nshipster-objc&quot;&gt;NSHipster post on it&lt;/a&gt;.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Test Post. Please Ignore.]]></title><description><![CDATA[Hey, I'm creating this to see if I can finally settle the toughs that come to me while I code away. I find that I should write down…]]></description><link>https://luisramos.dev/test-post-please-ignore</link><guid isPermaLink="false">https://luisramos.dev/test-post-please-ignore</guid><pubDate>Sun, 18 Oct 2015 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Hey, I&apos;m creating this to see if I can finally settle the toughs that come to me while I code away. I find that I should write down something so future me can make use of the troubles I go through, but the past me never got around to actually do something. Let&apos;s see if this time is the charm.&lt;/p&gt;
&lt;p&gt;I plan to write about software, mobile programming, the weather and whatever I feel like (that means gaming probably).&lt;/p&gt;
&lt;p&gt;Let&apos;s do this!&lt;/p&gt;</content:encoded></item></channel></rss>