<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Julien</title>
    <description>The latest articles on Forem by Julien (@ju-tngh).</description>
    <link>https://forem.com/ju-tngh</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F192567%2F9b542e27-1614-4e34-bb98-aec0585479bb.jpg</url>
      <title>Forem: Julien</title>
      <link>https://forem.com/ju-tngh</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/ju-tngh"/>
    <language>en</language>
    <item>
      <title>💡 Vue Typescript State Management : We can do better than “isLoading” in 2022</title>
      <dc:creator>Julien</dc:creator>
      <pubDate>Tue, 04 Jan 2022 10:57:22 +0000</pubDate>
      <link>https://forem.com/monisnap/vue-typescript-state-management-we-can-do-better-than-isloading-in-2022-2n5d</link>
      <guid>https://forem.com/monisnap/vue-typescript-state-management-we-can-do-better-than-isloading-in-2022-2n5d</guid>
      <description>&lt;p&gt;Sorry for the clickbait title, but I needed your attention 👀 &lt;/p&gt;

&lt;p&gt;Have you ever encountered code like this one :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Vue&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;el&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#app&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nf"&gt;data &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;info&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;loading&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;errored&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;filters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;currencydecimal &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toFixed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="nf"&gt;mounted &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;axios&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://api.coindesk.com/v1/bpi/currentprice.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;info&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bpi&lt;/span&gt;
      &lt;span class="p"&gt;})&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;errored&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
      &lt;span class="p"&gt;})&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;finally&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;loading&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="p"&gt;...&lt;/span&gt;

&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;app&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Bitcoin Price Index&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;section&lt;/span&gt; &lt;span class="na"&gt;v-if&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"errored"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;We're sorry, we're not able to retrieve this information at the moment, please try back later&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;section&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;section&lt;/span&gt; &lt;span class="na"&gt;v-else&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;v-if&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"loading"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Loading...&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;
      &lt;span class="na"&gt;v-else&lt;/span&gt;
      &lt;span class="na"&gt;v-for&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"currency in info"&lt;/span&gt;
      &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"currency"&lt;/span&gt;
    &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;currency&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;:
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"lighten"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt; &lt;span class="na"&gt;v-html&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"currency.symbol"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;currency&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rate_float&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;currencydecimal&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

  &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;section&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="na"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It’s an example I found in the Vue documentation here &lt;a href="https://vuejs.org/v2/cookbook/using-axios-to-consume-apis.html#Dealing-with-Errors" rel="noopener noreferrer"&gt;https://vuejs.org/v2/cookbook/using-axios-to-consume-apis.html#Dealing-with-Errors&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What if you have multiple things that could load, do you add a &lt;code&gt;loading2&lt;/code&gt; variable ? 👀&lt;/p&gt;

&lt;p&gt;To solve this issue, you can use a variable for each async actions you have with this 4 “states” :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;IDLE : the user didn’t trigger the action yet&lt;/li&gt;
&lt;li&gt;WAITING : the action is ongoing&lt;/li&gt;
&lt;li&gt;ERROR : there was an error&lt;/li&gt;
&lt;li&gt;DONE : The action succeeded&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Using an enum with the different states, and better naming, the code can be rewritten like this :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kr"&gt;enum&lt;/span&gt; &lt;span class="nx"&gt;AsyncState&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;IDLE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;IDLE&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;WAITING&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;WAITING&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;ERROR&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ERROR&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;DONE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;DONE&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Vue&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;el&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#app&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nf"&gt;data &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;info&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

      &lt;span class="nx"&gt;AsyncState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;currentPriceLoadState&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;AsyncState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;WAITING&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;filters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;currencydecimal &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toFixed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="nf"&gt;mounted &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;axios&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://api.coindesk.com/v1/bpi/currentprice.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;info&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bpi&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currentPriceLoadState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;AsyncState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DONE&lt;/span&gt;
      &lt;span class="p"&gt;})&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currentPriceLoadState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;AsyncState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ERROR&lt;/span&gt;
      &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="p"&gt;...&lt;/span&gt;

&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;app&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Bitcoin Price Index&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;v-if&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"currentPriceLoadState === AsyncState.WAITING"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Loading...&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;section&lt;/span&gt; &lt;span class="na"&gt;v-else-if&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"currentPriceLoadState === AsyncState.ERROR"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;We're sorry, we're not able to retrieve this information at the moment, please try back later&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;section&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;section&lt;/span&gt; &lt;span class="na"&gt;v-else&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;v-for&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"currency in info"&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"currency"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;currency&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;:
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"lighten"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt; &lt;span class="na"&gt;v-html&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"currency.symbol"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;currency&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rate_float&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;currencydecimal&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

  &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;section&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="na"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With better naming and this simple enum, you can almost cover all the use cases where you need to load one or multiple things and manage errors ✨&lt;/p&gt;

&lt;p&gt;If you want to manage different error messages, you can add another variable with an enum type like this :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kr"&gt;enum&lt;/span&gt; &lt;span class="nx"&gt;CurrentPriceLoadErrors&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;INVALID_CURRENCY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;INVALID_CURRENCY&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;API_LIMIT_REACHED&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;API_LIMIT_REACHED&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;DEFAULT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;DEFAULT&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Tell me in the comment section if you like this trick or not, and if you have an even better technique !&lt;/p&gt;

&lt;p&gt;And don’t forget to like and share this post if you liked it 💙&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>vue</category>
      <category>webdev</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>🚀 Flutter CI/CD using Git Tags in 5 minutes</title>
      <dc:creator>Julien</dc:creator>
      <pubDate>Fri, 14 May 2021 09:50:20 +0000</pubDate>
      <link>https://forem.com/monisnap/flutter-ci-cd-using-git-tags-in-5-minutes-5bb1</link>
      <guid>https://forem.com/monisnap/flutter-ci-cd-using-git-tags-in-5-minutes-5bb1</guid>
      <description>&lt;h2&gt;
  
  
  Table of contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;What you will need&lt;/li&gt;
&lt;li&gt;What you will do&lt;/li&gt;
&lt;li&gt;Step 1: Deployment script&lt;/li&gt;
&lt;li&gt;Step 2: Git push with tag&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What you will need &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;📲 A Flutter app (obviously)&lt;/li&gt;
&lt;li&gt;📦 A Github, Gitlab or Bitbucket account&lt;/li&gt;
&lt;li&gt;✨ A &lt;a href="https://codemagic.io/start/" rel="noopener noreferrer"&gt;Codemagic&lt;/a&gt; account (linked to your app repository)&lt;/li&gt;
&lt;li&gt;😁 Your best smile&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What you will do &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1a378jsuikoj6q03ilil.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1a378jsuikoj6q03ilil.png" alt="Alt Text" width="800" height="155"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;You will create a git tag containing your new app version, then push it to your repository. It will automatically trigger a Codemagic build, and release your app on the Play Store 🚀&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Create a deployment script 🛠 &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Below you'll find code to configure the CI/CD. You just have to add it to the root of your repository in a file named &lt;strong&gt;codemagic.yaml&lt;/strong&gt;&lt;br&gt;
&lt;em&gt;I've used a script instead of the workflow editor (Codemagic GUI) for multiple reasons (versioned, faster...) but mainly because the version handling isn't possible using the editor.&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# codemagic.yaml&lt;/span&gt;

&lt;span class="c1"&gt;# ... &amp;lt;- Here you will include the "reusable" parts that are described afterward&lt;/span&gt;

&lt;span class="na"&gt;workflows&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;play-store&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Play Store Release&lt;/span&gt;
    &lt;span class="na"&gt;max_build_duration&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;30&lt;/span&gt;
    &lt;span class="na"&gt;cache&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*caching&lt;/span&gt;

    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;flutter&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*flutter_version&lt;/span&gt;
      &lt;span class="na"&gt;xcode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;latest&lt;/span&gt;
      &lt;span class="na"&gt;cocoapods&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;default&lt;/span&gt;
      &lt;span class="na"&gt;vars&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*gcp_service_credentials&lt;/span&gt;
        &lt;span class="na"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*keystore_release&lt;/span&gt;

    &lt;span class="c1"&gt;# ! THE IMPORTANT PART IS HERE !&lt;/span&gt;
    &lt;span class="na"&gt;triggering&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;events&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;tag&lt;/span&gt;
      &lt;span class="na"&gt;branch_patterns&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;pattern&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;master"&lt;/span&gt;
          &lt;span class="na"&gt;include&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
          &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
      &lt;span class="na"&gt;tag_patterns&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;pattern&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;*"&lt;/span&gt;
          &lt;span class="na"&gt;include&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

    &lt;span class="na"&gt;scripts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="nv"&gt;*android_key_properties_setup&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="nv"&gt;*flutter_android_properties_setup&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="nv"&gt;*flutter_pub_get&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="nv"&gt;*flutter_test&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="nv"&gt;*flutter_build_play_store_release&lt;/span&gt;

    &lt;span class="na"&gt;artifacts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;      
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;build/**/outputs/**/*.aab&lt;/span&gt;

    &lt;span class="na"&gt;publishing&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;google_play&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;credentials&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*play_console_credentials&lt;/span&gt;
        &lt;span class="na"&gt;track&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;alpha&lt;/span&gt;
        &lt;span class="na"&gt;in_app_update_priority&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;0&lt;/span&gt;      
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next are the reusable parts, to be clean and not repeat yourself ✨. Replace the encrypted variables using your credentials and the &lt;a href="https://docs.codemagic.io/building/encrypting/" rel="noopener noreferrer"&gt;Codemagic encrypting tool&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# codemagic.yaml&lt;/span&gt;

&lt;span class="na"&gt;reusable&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;flutter_version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nl"&gt;&amp;amp;flutter_version&lt;/span&gt; &lt;span class="s"&gt;1.22.6&lt;/span&gt;

  &lt;span class="na"&gt;environment-variables&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="nl"&gt;&amp;amp;keystore_release&lt;/span&gt;
      &lt;span class="na"&gt;FCI_KEYSTORE_PATH&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/tmp/keystore.keystore&lt;/span&gt;
      &lt;span class="na"&gt;FCI_KEYSTORE&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Encrypted(...)&lt;/span&gt;
      &lt;span class="na"&gt;FCI_KEYSTORE_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Encrypted(...)&lt;/span&gt;
      &lt;span class="na"&gt;FCI_KEY_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Encrypted(...)&lt;/span&gt;
      &lt;span class="na"&gt;FCI_KEY_ALIAS&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Encrypted(...)&lt;/span&gt;

    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="nl"&gt;&amp;amp;gcp_service_credentials&lt;/span&gt;
      &lt;span class="na"&gt;GCLOUD_SERVICE_ACCOUNT_CREDENTIALS&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Encrypted(...)&lt;/span&gt;      

    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="nl"&gt;&amp;amp;play_console_credentials&lt;/span&gt; &lt;span class="s"&gt;Encrypted(...)&lt;/span&gt;

  &lt;span class="na"&gt;scripts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="nl"&gt;&amp;amp;android_key_properties_setup&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Android - Setup key.properties&lt;/span&gt;
      &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
        &lt;span class="s"&gt;echo $FCI_KEYSTORE | base64 --decode &amp;gt; $FCI_KEYSTORE_PATH&lt;/span&gt;
        &lt;span class="s"&gt;cat &amp;gt;&amp;gt; "$FCI_BUILD_DIR/android/key.properties" &amp;lt;&amp;lt;EOF&lt;/span&gt;
        &lt;span class="s"&gt;storePassword=$FCI_KEYSTORE_PASSWORD&lt;/span&gt;
        &lt;span class="s"&gt;keyPassword=$FCI_KEY_PASSWORD&lt;/span&gt;
        &lt;span class="s"&gt;keyAlias=$FCI_KEY_ALIAS&lt;/span&gt;
        &lt;span class="s"&gt;storeFile=/tmp/keystore.keystore&lt;/span&gt;
        &lt;span class="s"&gt;EOF&lt;/span&gt;

    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="nl"&gt;&amp;amp;flutter_android_properties_setup&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Flutter x Android - Setup local.properties&lt;/span&gt;
      &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;echo "flutter.sdk=$HOME/programs/flutter" &amp;gt; "$FCI_BUILD_DIR/android/local.properties"&lt;/span&gt;

    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="nl"&gt;&amp;amp;flutter_pub_get&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Flutter - Get dependencies&lt;/span&gt;
      &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;flutter packages pub get&lt;/span&gt;

    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="nl"&gt;&amp;amp;flutter_test&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Flutter - Run tests&lt;/span&gt;
      &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;flutter test --machine&lt;/span&gt;

    &lt;span class="c1"&gt;# ! THE IMPORTANT PART IS HERE !&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="nl"&gt;&amp;amp;flutter_build_play_store_release&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build AAB for Play Store release&lt;/span&gt;
      &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
        &lt;span class="s"&gt;GCLOUD_SERVICE_ACCOUNT_CREDENTIALS=$(echo $GCLOUD_SERVICE_ACCOUNT_CREDENTIALS | base64 --decode)&lt;/span&gt;
        &lt;span class="s"&gt;NEW_BUILD_NUMBER=$(($(google-play get-latest-build-number --package-name 'com.company.example') + 1))&lt;/span&gt;
        &lt;span class="s"&gt;NEW_VERSION_NAME=$(git describe --tags)&lt;/span&gt;

        &lt;span class="s"&gt;echo $NEW_VERSION_NAME&lt;/span&gt;
        &lt;span class="s"&gt;echo $NEW_BUILD_NUMBER&lt;/span&gt;

        &lt;span class="s"&gt;flutter build appbundle --build-name=$NEW_VERSION_NAME --build-number=$NEW_BUILD_NUMBER  --obfuscate --split-debug-info=$FCI_BUILD_DIR/debug_files&lt;/span&gt;

  &lt;span class="na"&gt;caching&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nl"&gt;&amp;amp;caching&lt;/span&gt;
    &lt;span class="na"&gt;cache_paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;$HOME/.gradle/caches&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;$FLUTTER_ROOT/.pub-cache&lt;/span&gt;

&lt;span class="c1"&gt;# ... &amp;lt;- The workflow part described before should be here&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the previous script, the &lt;strong&gt;"flutter_build_play_store_release"&lt;/strong&gt; script handle the versioning :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Version&lt;/strong&gt; is retrieved from the tag.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Build number&lt;/strong&gt; is retrieved from the play console (fetching the highest build number, and incrementing it for this release).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Don't forget to replace &lt;strong&gt;com.company.example&lt;/strong&gt; with your app package name&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;You can now push this script onto your repository. It should then be visible from Codemagic and you should be able to trigger it manually.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Add git tag 🔖 -&amp;gt; push it 🚀 &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Creating a git tag is very easy. We will use 1.0.0 as our version, and tag.&lt;br&gt;
You just have to run the following commands :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git tag &lt;span class="nt"&gt;-a&lt;/span&gt; 1.0.0 &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"Release 1.0.0"&lt;/span&gt;
git push origin 1.0.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it! You should see a build running in Codemagic 🏄‍♂️&lt;/p&gt;

&lt;h3&gt;
  
  
  What's next?
&lt;/h3&gt;

&lt;p&gt;You can add as many workflows as you want using Codemagic! You can use the GUI editor, and then extract to code to have more control over the code. &lt;/p&gt;

&lt;p&gt;Here we deploy on Alpha, so you can change to Production when you feel ready. And then, you can add an iOS workflow with mostly the same code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Leave a comment, a like or even a unicorn 🦄 if you've achieved this tutorial! 🚀🔖&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>flutter</category>
      <category>git</category>
      <category>codemagic</category>
      <category>android</category>
    </item>
    <item>
      <title>⏱ 5 min chrono FREE admin panel with ForestAdmin</title>
      <dc:creator>Julien</dc:creator>
      <pubDate>Fri, 17 Jan 2020 11:15:17 +0000</pubDate>
      <link>https://forem.com/monisnap/5-min-chrono-admin-panel-with-forestadmin-6-months-later-583</link>
      <guid>https://forem.com/monisnap/5-min-chrono-admin-panel-with-forestadmin-6-months-later-583</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Product guy :&lt;/strong&gt; Hey, could you give me access to database ?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tech guy :&lt;/strong&gt; Hmm.. why ?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Product guy :&lt;/strong&gt; To avoid to bother you each time I need to update something&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tech guy :&lt;/strong&gt; So, you would need a BackOffice for that.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Product guy :&lt;/strong&gt; Ok, could you please develop it ?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You probably already had this discussion...&lt;br&gt;
Let's imagine now that you can solve it ... in MINUTES !&lt;/p&gt;

&lt;h1&gt;
  
  
  What is &lt;a href="https://www.forestadmin.com/" rel="noopener noreferrer"&gt;ForestAdmin&lt;/a&gt; 🌲 ?
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;"Forest Admin provides an off-the-shelf admin panel based on a highly-extensible API hosted on your servers."&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In other words, it's a SaaS platform providing all common admin tasks you'll need as developer if you want to create your own Back Office.&lt;/p&gt;

&lt;p&gt;With Forest you can :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;📝 View / Create / Edit / Delete entries of your database&lt;/li&gt;
&lt;li&gt;🔐 Handle access control list&lt;/li&gt;
&lt;li&gt;📈 Create awesome dashboards and views with 0 or few code lines.&lt;/li&gt;
&lt;li&gt;🌍 Create custom actions that triggers your backend API&lt;/li&gt;
&lt;li&gt;✨ And many more !&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All of this only with configurations and few lines of Javascript 👨‍💻!&lt;/p&gt;

&lt;p&gt;Here is an example of what it looks like out of the box :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.forestadmin.com%2Fpublic%2Fimg%2Fillustrations-dev%2Fscreens%2Fcrud.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.forestadmin.com%2Fpublic%2Fimg%2Fillustrations-dev%2Fscreens%2Fcrud.jpg" alt="OutOfTheBox" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Installation
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Create your account for FREE at &lt;a href="https://www.forestadmin.com/" rel="noopener noreferrer"&gt;https://www.forestadmin.com/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Follow the instructions provided to connect to your database*&lt;/li&gt;
&lt;li&gt;Install in local using Docker or Node.js&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;npm start&lt;/code&gt; 🚀&lt;/li&gt;
&lt;li&gt;Go to &lt;a href="http://app.forestadmin.com/projects" rel="noopener noreferrer"&gt;http://app.forestadmin.com/projects&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;*&lt;em&gt;For the time being, you can only connect to Mysql, PostgreSQL, SQL Server and MongoDB.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Deployment
&lt;/h2&gt;

&lt;p&gt;You can host your server &lt;strong&gt;anywhere you want&lt;/strong&gt; using &lt;strong&gt;Docker&lt;/strong&gt; or &lt;strong&gt;Node.js&lt;/strong&gt;. For example, Forest provide a tutorial to host your server on Heroku &lt;a href="https://docs.forestadmin.com/documentation/how-tos/deploy-to-production-on-heroku" rel="noopener noreferrer"&gt;here&lt;/a&gt; (it takes really 3 minutes !)&lt;/p&gt;

&lt;h2&gt;
  
  
  Customization
&lt;/h2&gt;

&lt;p&gt;You have 2 categories of customization :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Model&lt;/strong&gt; : You can modify your database model, relationships and custom actions (called "smart actions") using Javascript !&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;UI&lt;/strong&gt; : That's the best part for developers. You can modify your UI using the GUI layout editor provided by forest admin (based on &lt;a href="https://emberjs.com/" rel="noopener noreferrer"&gt;ember.js&lt;/a&gt;) &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here are some examples :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvrw5yqtxpc81dzeewjsp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvrw5yqtxpc81dzeewjsp.png" alt="Admin panel" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can even develop your own UI with HTML/CSS/JS code to have custom views like this one :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3wa8ae0rurn0r79jys08.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3wa8ae0rurn0r79jys08.png" alt="Admin panel" width="800" height="403"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How it works ?
&lt;/h2&gt;

&lt;p&gt;It's quite interesting to know how the product is made because it's atypical. Indeed, the code and model is hosted on your servers but the UI layout configuration is hosted on Forest servers. Here is a schema of the architecture :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.forestadmin.com%2Fpublic%2Fimg%2Fillustrations-dev%2Fschema-4.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.forestadmin.com%2Fpublic%2Fimg%2Fillustrations-dev%2Fschema-4.svg" alt="Architecture" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This way, your data remain secure 🔐&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;It's been 6 months that we use Forest and the whole team is very happy with it. The product and marketing love the dashboard and custom views (you know, they love fancy graphs 😄). The customer support work is easier thanks to views displaying customer data to answer questions quickly.&lt;/p&gt;

&lt;p&gt;Also, it's free up to 10 users so you can test it before using it in your profesional projects !&lt;/p&gt;

&lt;p&gt;And above all, it doesn't take time to your dev team, and they can use time for "real work" ^^&lt;/p&gt;

&lt;p&gt;Hope it helps !&lt;/p&gt;

</description>
      <category>database</category>
      <category>javascript</category>
      <category>webdev</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Github private package with CI/CD</title>
      <dc:creator>Julien</dc:creator>
      <pubDate>Fri, 03 Jan 2020 12:33:28 +0000</pubDate>
      <link>https://forem.com/monisnap/github-private-package-with-ci-cd-4k0</link>
      <guid>https://forem.com/monisnap/github-private-package-with-ci-cd-4k0</guid>
      <description>&lt;p&gt;With the newly created &lt;a href="https://github.com/features/packages" rel="noopener noreferrer"&gt;Github Package Registry&lt;/a&gt;, you can store your private packages in the same place as your code.&lt;/p&gt;

&lt;p&gt;Deploying packages and use them inside a &lt;code&gt;package.json&lt;/code&gt; file is very simple. When it comes to use them in a CI/CD environment it's a bit tricky.&lt;/p&gt;

&lt;p&gt;First, we need to provide our ci/cd environment a secure way to retrieve packages from our private repositories. Using the classic &lt;code&gt;npm login&lt;/code&gt; is secure but not very handy. So the &lt;strong&gt;TOKEN&lt;/strong&gt; approach is what I chose. See more info about creation &lt;a href="https://help.github.com/en/github/authenticating-to-github/creating-a-personal-access-token-for-the-command-line" rel="noopener noreferrer"&gt;here&lt;/a&gt;. You only need to provide &lt;strong&gt;read access to packages&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Then we need to set a reference to our private repositories for npm to resolve and find it. The easiest way is to set an &lt;strong&gt;.npmrc&lt;/strong&gt; file at the root of your project and put things this way :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;registry&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;https://registry.npmjs.org/
@owner:registry&lt;span class="o"&gt;=&lt;/span&gt;https://npm.pkg.github.com/
//npm.pkg.github.com/:_authToken&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;GITHUB_TOKEN&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The trick here is to indicate the default registry (registry.npmjs.org) for all other public packages you want to use.&lt;/p&gt;

&lt;p&gt;Be sure to replace &lt;code&gt;owner&lt;/code&gt; by your organisation name. The &lt;code&gt;GITHUB_TOKEN&lt;/code&gt; is stored as environment variable (you have many ways to hide it on your machine and in some ci/cd tools, don't need to hardcode it).&lt;/p&gt;

&lt;p&gt;Hope it helps !&lt;/p&gt;

</description>
      <category>github</category>
      <category>npm</category>
      <category>devops</category>
    </item>
  </channel>
</rss>
