<?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>DEV Community: Thibault Maekelbergh</title>
    <description>The latest articles on DEV Community by Thibault Maekelbergh (@thibmaek).</description>
    <link>https://dev.to/thibmaek</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%2F11534%2Fe849ab2b-38f7-4de9-ac69-e27efd5ea704.png</url>
      <title>DEV Community: Thibault Maekelbergh</title>
      <link>https://dev.to/thibmaek</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/thibmaek"/>
    <language>en</language>
    <item>
      <title>Single point versioning with Fastlane for React Native</title>
      <dc:creator>Thibault Maekelbergh</dc:creator>
      <pubDate>Mon, 31 Jan 2022 08:39:40 +0000</pubDate>
      <link>https://dev.to/inthepocket/single-point-versioning-with-fastlane-for-react-native-2he4</link>
      <guid>https://dev.to/inthepocket/single-point-versioning-with-fastlane-for-react-native-2he4</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;This post originally appeared on our &lt;a href="https://inthepocket.dev/posts/2022-01-27-single-point-versioning-with-fastlane-for-react-native/"&gt;ITP Dev blog&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The issue
&lt;/h2&gt;

&lt;p&gt;When upgrading (or "bumping") a React native app you often need to touch multiple files. You need to change the version in the node package manifests, update that same version and the build id in every Xcode scheme you use, and on Android either set the version via &lt;code&gt;gradle.properties&lt;/code&gt; or via CLI flags when executing Gradle.&lt;/p&gt;

&lt;p&gt;Previously at ITP we tackled this with a conceptually simple bash script. You supplied it the current version and desired version and it did basic find and replace with &lt;code&gt;sed&lt;/code&gt;:&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="c"&gt;#!/usr/bin/env bash&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt;

&lt;span class="k"&gt;function &lt;/span&gt;_replace_in_file&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="nb"&gt;command&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /dev/null&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"s/&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;$2&lt;/span&gt;&lt;span class="s2"&gt;/g"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$3&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="k"&gt;else
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"You don't have sed and this script relies on it."&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Please install sed via apt or brew"&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
  &lt;span class="k"&gt;fi&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;function &lt;/span&gt;bump_android&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Updating gradle.properties"&lt;/span&gt;

  _replace_in_file &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$2&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; ./android/gradle.properties
  &lt;span class="c"&gt;# shellcheck disable=SC2001&lt;/span&gt;
  _replace_in_file &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s1"&gt;'s/\.//g'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;0"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$2&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s1"&gt;'s/\.//g'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="se"&gt;\0&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; ./android/gradle.properties
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;function &lt;/span&gt;bump_ios&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;schemes&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;&lt;span class="s2"&gt;"appstore"&lt;/span&gt; &lt;span class="s2"&gt;"client"&lt;/span&gt; &lt;span class="s2"&gt;"dev"&lt;/span&gt; &lt;span class="s2"&gt;"release"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;scheme &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;schemes&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Updating Xcode scheme: &lt;/span&gt;&lt;span class="nv"&gt;$scheme&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    _replace_in_file &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$2&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"./ios/RNApp/Info-&lt;/span&gt;&lt;span class="nv"&gt;$scheme&lt;/span&gt;&lt;span class="s2"&gt;.plist"&lt;/span&gt;
  &lt;span class="k"&gt;done&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;function &lt;/span&gt;bump_node&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Updating package.json, package-lock.json"&lt;/span&gt;

  _replace_in_file &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;version&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;version&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="nv"&gt;$2&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; package.json

  &lt;span class="c"&gt;# Regenerate package-lock&lt;/span&gt;
  npm i &lt;span class="nt"&gt;--ignore-scripts&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;function &lt;/span&gt;main&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$2&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Commands supplied were not valid."&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"This script expects the following form:"&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"  &lt;/span&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="s2"&gt;./bump-version.sh &amp;lt;old_version&amp;gt; &amp;lt;new_version&amp;gt;"&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;1
  &lt;span class="k"&gt;fi

  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Performing version bump from &lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt; 👉 &lt;/span&gt;&lt;span class="nv"&gt;$2&lt;/span&gt;&lt;span class="s2"&gt;..."&lt;/span&gt;

  bump_node &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$@&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  bump_android &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$@&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  bump_ios &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$@&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

  &lt;span class="c"&gt;# sed outputs some clutter files suffixed with -e, we delete them with xargs&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;uname&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"Linux"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;find &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;-type&lt;/span&gt; f &lt;span class="nt"&gt;-name&lt;/span&gt; &lt;span class="se"&gt;\*&lt;/span&gt;&lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nt"&gt;-print0&lt;/span&gt; | xargs &lt;span class="nt"&gt;--null&lt;/span&gt; /bin/rm &lt;span class="nt"&gt;-rf&lt;/span&gt;
  &lt;span class="k"&gt;else
    &lt;/span&gt;find &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;-type&lt;/span&gt; f &lt;span class="nt"&gt;-name&lt;/span&gt; &lt;span class="se"&gt;\*&lt;/span&gt;&lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nt"&gt;-print0&lt;/span&gt; | /usr/bin/xargs &lt;span class="nt"&gt;-0&lt;/span&gt; /bin/rm &lt;span class="nt"&gt;-rf&lt;/span&gt;
  &lt;span class="k"&gt;fi&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

main &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$@&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, for people not familiar with Bash this script is some advanced dark magic. It was also reliant on which &lt;code&gt;sed&lt;/code&gt; you have available on the system, GNU or BSD (see the check at the end of the script). The files changed were standards for iOS &amp;amp; node, but for Android we used an older way of putting the version in a variable inside of &lt;code&gt;gradle.properties&lt;/code&gt; and then read that variable during the &lt;code&gt;gradle&lt;/code&gt; assemble task.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why automate
&lt;/h2&gt;

&lt;p&gt;Why automate something you could just change in VS Code or Xcode in the relative files? Well, version upgrades might not happen that often and then you need to either take your chances or ask around with coworkers if they remember the files that need to be changed. It's also super fast for newcomers in the team or to native development to just run the script (however they won't know the deeper details of what's happening).&lt;/p&gt;

&lt;p&gt;You could, if desired, also just run this in a CI pipeline and have CI take care of automating version upgrades instead of making it a semi-automatic process via script.&lt;/p&gt;

&lt;p&gt;This worked great for a few years, but when I came to think about it this way is still a bit to over engineered. We already use the great Fastlane tool in all our projects for building iOS &amp;amp; Android so surely there must be a way to make it easier &lt;strong&gt;and&lt;/strong&gt; integrated with Fastlane.&lt;/p&gt;

&lt;h2&gt;
  
  
  Single point versioning via Fastlane
&lt;/h2&gt;

&lt;p&gt;The solution to this in Fastlane is really easy:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Use Fastlane's &lt;code&gt;load_json&lt;/code&gt; plugin and read node's &lt;code&gt;package.json&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Get the semver version e.g &lt;code&gt;1.0.0&lt;/code&gt; from the &lt;code&gt;version&lt;/code&gt; property&lt;/li&gt;
&lt;li&gt;Append a unique build identifier. We use the job ID from our CI/CD pipeline&lt;/li&gt;
&lt;li&gt;Join version and build id to a string&lt;/li&gt;
&lt;li&gt;Set that as the version in Fastlane &lt;code&gt;gradle&lt;/code&gt; &amp;amp; &lt;code&gt;gym&lt;/code&gt; tasks.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This gives us a single point to manage the version: &lt;code&gt;package.json&lt;/code&gt;. Updating the version there makes Fastlane read that and supply it to gym/gradle relative commands.&lt;br&gt;
We abstracted this to a &lt;code&gt;utils.rb&lt;/code&gt; ruby module since we use different schemes and build configurations, but if you have a simpler app it could just as well be written inline or at the top of your Fastfile&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;utils.rb&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;load_build_metadata&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;package&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;load_json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;json_path: &lt;/span&gt;&lt;span class="s1"&gt;'./package.json'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;build_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'AZURE_UNIQUE_BUILD_ID'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;build_id&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;nil?&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;build_id&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;empty?&lt;/span&gt;
    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"Required environment variable 'AZURE_UNIQUE_BUILD_ID' not found. Got: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;build_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="nb"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;app_version&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;package&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'version'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="n"&gt;build_number&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;build_id&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;gsub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'.'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# We expect a date format like 20211122.6 here and trim the dot&lt;/span&gt;

  &lt;span class="c1"&gt;# Return a hashmap with all version metadata we could be interested in&lt;/span&gt;
  &lt;span class="no"&gt;Hash&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s1"&gt;'build_number'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;build_number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'app_version'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;app_version&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'full_version'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;app_version&lt;/span&gt;&lt;span class="si"&gt;}#{&lt;/span&gt;&lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:suffix&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;build_number&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Fastfile&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="p"&gt;platform :ios do
&lt;/span&gt;  desc '📱🥉 Build iOS Client'
  lane :build_client do
    add_app_icon_badge(label: 'client')
&lt;span class="gi"&gt;+   build_metadata = load_build_metadata(suffix: opts[:suffix])
&lt;/span&gt;
    update_settings_bundle(
      xcodeproj: "ios/RNApp.xcodeproj",
      configuration: 'Client',
      target: 'RNApp-iOS',
      key: 'version_preference',
&lt;span class="gi"&gt;+     value: build_metadata['full_version']
&lt;/span&gt;    )

    set_info_plist_value(
      path: "ios/RNApp-iOS/Info-Client.plist",
      key: 'CFBundleShortVersionString',
&lt;span class="gi"&gt;+     value: build_metadata['app_version']
&lt;/span&gt;    )

    set_info_plist_value(
      path: "ios/RNApp-iOS/Info-Client.plist",
      key: 'CFBundleVersion',
&lt;span class="gi"&gt;+     value: build_metadata['full_version']
&lt;/span&gt;    )

    gym(...)
  end
&lt;span class="p"&gt;end
&lt;/span&gt;
platform :android do
  desc '🤖🥉 Build Android Client'
  lane :build_client do
    add_app_icon_badge(label: 'client')
&lt;span class="gi"&gt;+   build_metadata = load_build_metadata(suffix: opts[:suffix])
&lt;/span&gt;
    gradle(project_dir: 'android', task: 'clean')

    gradle(
      project_dir: 'android',
      task: 'assemble',
      build_type: 'Clientrelease',
      properties: {
&lt;span class="gi"&gt;+       "android.injected.version.name": build_metadata['full_version'],
+       "android.injected.version.code": build_metadata['build_number']
&lt;/span&gt;      }
    )
  end
&lt;span class="p"&gt;end
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;&lt;strong&gt;We now just need to do &lt;code&gt;npm version minor&lt;/code&gt; and commit that the branch so the CI pipeline can pick it up 👷🏻‍♂️&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;And then to summarise, this way of versioning multiple configurations/schemes is better for us because:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We stopped using &lt;code&gt;gradle.properties&lt;/code&gt; to version Android and inject it into Gradle by reading from the properties file! Instead we now use CLI flags passed to Gradle under the hood to configure versions at build time rather than in code.&lt;/li&gt;
&lt;li&gt;Creating a new version is actually handled by the one tool in control: Fastlane. The need to manually bump the correct files is gone. We narrowed it down to one: &lt;code&gt;package.json&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;This works regardless of which OS you are running on or which version/flavour of bash and subtools you use (e.g &lt;code&gt;sed&lt;/code&gt;, &lt;code&gt;xargs&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;It's more declarative than the bash script which required some knowledge of bash. That's not a common thing.&lt;/li&gt;
&lt;li&gt;The file typically doesn't change a lot, and if it does, anyone can easily solve/adapt it.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>fastlane</category>
      <category>reactnative</category>
      <category>xcode</category>
      <category>gradle</category>
    </item>
    <item>
      <title>"Fixing" React Navigation &amp; iOS screen reader focus</title>
      <dc:creator>Thibault Maekelbergh</dc:creator>
      <pubDate>Thu, 08 Apr 2021 09:14:41 +0000</pubDate>
      <link>https://dev.to/inthepocket/fixing-react-navigation-ios-screen-reader-focus-7op</link>
      <guid>https://dev.to/inthepocket/fixing-react-navigation-ios-screen-reader-focus-7op</guid>
      <description>&lt;h2&gt;
  
  
  The problem
&lt;/h2&gt;

&lt;p&gt;During some extensive &lt;strong&gt;accessibility&lt;/strong&gt; (mainly screen reader) testing on a React Native app we're currently developing, we bumped into an issue where &lt;strong&gt;focus would be lost&lt;/strong&gt; for the screen reader when navigating to a &lt;strong&gt;new view in a stack&lt;/strong&gt; using &lt;strong&gt;React Navigation&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;You would transition from the overview to a detail page, but instead of &lt;strong&gt;respecting the navigation tree&lt;/strong&gt; for that page, React Navigation would &lt;strong&gt;focus the screen reader&lt;/strong&gt; on the position where your &lt;strong&gt;focus was in the previous screen&lt;/strong&gt;. This would pose a big problem of course: Imagine scrolling through a long list (&lt;code&gt;FlatList&lt;/code&gt;), then navigating to the detail for the item you tapped, and having the screen reader focus &lt;strong&gt;somewhere on the middle of the screen&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This issue was however &lt;strong&gt;only happening on iOS&lt;/strong&gt;. My guess was that React Navigation in some way does not respect the iOS native navigation elements and does some JS things inbetween, which makes iOS act up in this case.&lt;/p&gt;

&lt;h2&gt;
  
  
  The "fix"
&lt;/h2&gt;

&lt;p&gt;To fix it, we created the &lt;code&gt;useAccessibilityFocus&lt;/code&gt; hook! It’s very simple and will just return a &lt;code&gt;ref&lt;/code&gt; to bind to your element, and a function to trigger focus to the element carrying the ref:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;MutableRefObject&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useCallback&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useRef&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;AccessibilityInfo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;findNodeHandle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Platform&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-native&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * Returns a ref object which when bound to an element, will focus that
 * element in VoiceOver/TalkBack on its appearance
 */&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;useAccessibilityFocus&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;MutableRefObject&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;any&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;Void&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ref&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useRef&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;setFocus&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useCallback&lt;/span&gt;&lt;span class="p"&gt;(()&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;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Platform&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;OS&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ios&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;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;focusPoint&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;findNodeHandle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;focusPoint&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nx"&gt;AccessibilityInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setAccessibilityFocus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;focusPoint&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="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;ref&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="nx"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setFocus&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;Now we can &lt;strong&gt;call it in a &lt;code&gt;useEffect&lt;/code&gt;&lt;/strong&gt; or even better, React Navigation's &lt;strong&gt;&lt;code&gt;useFocusEffect&lt;/code&gt;&lt;/strong&gt; to focus on the element when the screen appears:&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;DetailScreen&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;navigation&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;focusRef&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setFocus&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useAccessibilityFocus&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="nx"&gt;useFocusEffect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;setFocus&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="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;View&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="nc"&gt;TitleInput&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="nc"&gt;View&lt;/span&gt; &lt;span class="na"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;focusRef&lt;/span&gt;&lt;span class="si"&gt;}&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="nc"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Content I want the focus on&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Text&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="nc"&gt;View&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="nc"&gt;View&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;Another way would be to &lt;strong&gt;hook into a listener&lt;/strong&gt; on your stack’s screens:&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;RootNav&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;focusRef&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setFocus&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useAccessibilityFocus&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="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Stack&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Navigator&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="nc"&gt;Stack&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Screen&lt;/span&gt; &lt;span class="na"&gt;component&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;OverviewScreen&lt;/span&gt;&lt;span class="si"&gt;}&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="nc"&gt;Stack&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Screen&lt;/span&gt;
        &lt;span class="na"&gt;component&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;DetailScreen&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;header&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;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;NavHeader&lt;/span&gt; &lt;span class="na"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;focusRef&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Detail"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="na"&gt;listeners&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;transitionEnd&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;gt;&lt;/span&gt; &lt;span class="nx"&gt;setFocus&lt;/span&gt;&lt;span class="p"&gt;()&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;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Stack&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Navigator&lt;/span&gt;&lt;span class="p"&gt;&amp;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;Worth noting that now, we have the &lt;strong&gt;added benefit&lt;/strong&gt; that we can use this hook in &lt;strong&gt;other situations&lt;/strong&gt;! Rather than moving to another view in the stack, we can now use this to &lt;strong&gt;trigger screen reader focus to modals, notifications, alerts, errors etc.&lt;/strong&gt; Just be sure to remove the &lt;code&gt;Platform.OS&lt;/code&gt; check in the hook in that case&lt;/p&gt;

&lt;h2&gt;
  
  
  The solution
&lt;/h2&gt;

&lt;p&gt;The &lt;strong&gt;real fix&lt;/strong&gt; however, is that React Navigation fixes this issue in their &lt;strong&gt;core implementation&lt;/strong&gt; so that focus is carried along when navigating to another screen. A &lt;a href="https://github.com/react-navigation/react-navigation/issues/7056"&gt;PR discussion about this&lt;/a&gt; is open, you can even see yours truly in the discussion, but sadly no development towards fixing this has been done I guess.&lt;/p&gt;

</description>
      <category>reactnative</category>
      <category>reactnavigation</category>
      <category>a11y</category>
    </item>
    <item>
      <title>Why does typescript not conditionally pick the right type in a union?</title>
      <dc:creator>Thibault Maekelbergh</dc:creator>
      <pubDate>Fri, 10 Jan 2020 14:05:29 +0000</pubDate>
      <link>https://dev.to/thibmaek/why-does-typescript-not-conditionally-pick-the-right-type-in-a-union-4bc7</link>
      <guid>https://dev.to/thibmaek/why-does-typescript-not-conditionally-pick-the-right-type-in-a-union-4bc7</guid>
      <description>&lt;p&gt;Why does the | (Union) operator not work as a logical or?&lt;/p&gt;

&lt;p&gt;In the following snippets I would've expected it to see that the &lt;code&gt;name&lt;/code&gt; property is available in 1 of the provided union options? Instead it errors that it can't find name because it is not available in &lt;code&gt;MyDevice&lt;/code&gt; but it is available in the interface provided in the union.&lt;/p&gt;

&lt;p&gt;How do you go about conditionally deciding to pick the interface instead of MyDevice when the &lt;code&gt;name&lt;/code&gt; property is available?&lt;/p&gt;

&lt;p&gt;I know conditional types exist but that doesn't seem like an option because I'm providing plain interfaces and not types/interfaces with generics.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--dBrCLNBo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.imgur.com/4illPLh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--dBrCLNBo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://i.imgur.com/4illPLh.png" alt="img"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4eAgev3e--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/1jya7fmn5ae1wz0wqaiz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4eAgev3e--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/1jya7fmn5ae1wz0wqaiz.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>help</category>
      <category>question</category>
    </item>
    <item>
      <title>I'm writing a bash course</title>
      <dc:creator>Thibault Maekelbergh</dc:creator>
      <pubDate>Sat, 16 Mar 2019 14:38:29 +0000</pubDate>
      <link>https://dev.to/thibmaek/im-writing-a-bash-cours-490b</link>
      <guid>https://dev.to/thibmaek/im-writing-a-bash-cours-490b</guid>
      <description>&lt;p&gt;I love bash. For me it is the most powerful language I have ever used. But unfortunately it has a very difficult syntax and concepts which confuse the hell out of me time and time again.&lt;/p&gt;

&lt;p&gt;This course started as a way for me to have a reference to all the things I keep forgetting or need to refresh syntax-wise before I use them in a script or automation. I was getting a lot of positive feedback from friends and coworkers about my ability to use bash scripts as automations or to perform difficult (manual) tasks, and that they found it difficult to start writing scripts themselves.&lt;/p&gt;

&lt;p&gt;That's when I decided that this course shouldn't be just a catch-all or notepad for my thoughts but something more structured which offers a simple way and examples of how to do things in bash or use certain patterns.&lt;/p&gt;

&lt;p&gt;I'm currently writing this course on Notion and it live updates, I'll try to do my best to update it regularly. If you want you can see what I'm doing here and comment on it!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.notion.so/thibmaek/Bash-shell-scripting-bef80e00b7994a9bb1228f97594430a8"&gt;https://www.notion.so/thibmaek/Bash-shell-scripting-bef80e00b7994a9bb1228f97594430a8&lt;/a&gt;&lt;/p&gt;

</description>
      <category>bash</category>
    </item>
    <item>
      <title>🐳 Migrating InfluxDB databases to docker-compose</title>
      <dc:creator>Thibault Maekelbergh</dc:creator>
      <pubDate>Tue, 26 Feb 2019 14:20:09 +0000</pubDate>
      <link>https://dev.to/thibmaek/-migrating-influxdb-databases-to-docker-compose-2kee</link>
      <guid>https://dev.to/thibmaek/-migrating-influxdb-databases-to-docker-compose-2kee</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;This blogpost was originally publish on my &lt;a href="https://thibmaek.com/post/migrating-influxdb-databases-to-docker-compose"&gt;blog&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1&gt;
  
  
  The problem
&lt;/h1&gt;

&lt;p&gt;I got a brand new Intel NUC which I'm planning to use as a server with Docker. Some weeks earlier I had set up InfluxDB on a Raspberry Pi Zero W to collects my Home Assistant data and monitor my machines with Telegraf.&lt;/p&gt;

&lt;p&gt;Incorrectly, I guessed that there would be an import/export module in Chronograf which I use to manage my InfluxDB database. The problem seems to be a bit more complicated though. The problem itself lies in the fact that the influxd service needs to be halted before the restoration of a database can happen and to replicate that scenario, you'd have to shut down the Docker container... and also making it useless to act upon.&lt;/p&gt;

&lt;p&gt;Follow along for the quickest solution I've found using docker-compose and an intermediate container&lt;/p&gt;

&lt;h2&gt;
  
  
  Backing up
&lt;/h2&gt;

&lt;p&gt;On the old host running InfluxDB (Rpi Zero) stop the service and export the databases. The most recent and recommend way of doing so is via the portable method:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Ignore this if the service is already running of course.&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl start influxdb
&lt;span class="nv"&gt;$ &lt;/span&gt;influxd backup &lt;span class="nt"&gt;-portable&lt;/span&gt; &lt;span class="nt"&gt;-database&lt;/span&gt; telegraf ./influxdb-backup/telegraf
&lt;span class="nv"&gt;$ &lt;/span&gt;influxd backup &lt;span class="nt"&gt;-portable&lt;/span&gt; &lt;span class="nt"&gt;-database&lt;/span&gt; home_assistant ./influxdb-backup/home_assistant
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl stop influxdb
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;After the two databases were backed up, I copied those directories over with scp and then copied them back to NUC where I would run the Docker container&lt;/p&gt;

&lt;h2&gt;
  
  
  Restoring
&lt;/h2&gt;

&lt;p&gt;Now I had my InfluxDB container already running to test the connection with Chronograf. Since I think the format is more readable, I use docker-compose to spin up these containers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;2'&lt;/span&gt;

&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;influxdb&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;influxdb:latest&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;influxdb&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;8086:8086&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/opt/appdata/influxdb:/var/lib/influxdb&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="s"&gt;always&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The steps to restoring include first shutting down this container and then spinning up a throw-away container to copy the backup directories to the locally mounted volume /opt/appdata which will persist in the docker-compose created container.&lt;/p&gt;

&lt;p&gt;First, stop our current container with &lt;code&gt;docker-compose down&lt;/code&gt; (or &lt;code&gt;docker stop influxdb&lt;/code&gt;)&lt;/p&gt;

&lt;p&gt;Create intermediate container that maps the data volume and our backup directory and then enter the container's shell:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;--detach&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; /opt/appdata/influxdb:/var/lib/influxdb &lt;span class="nt"&gt;-v&lt;/span&gt; /home/thibmaek/influxdb-backup:/backups &lt;span class="nt"&gt;-p&lt;/span&gt; 8086 influxdb:latest
&lt;span class="nv"&gt;$ &lt;/span&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; mystifying_kepler /bin/bash
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Inside the intermediate container use the mapped backup folder to restore to our database and exit it afterwards:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;influxd restore &lt;span class="nt"&gt;-portable&lt;/span&gt; &lt;span class="nt"&gt;-database&lt;/span&gt; telegraf /backups/telegraf
&lt;span class="nv"&gt;$ &lt;/span&gt;influxd restore &lt;span class="nt"&gt;-portable&lt;/span&gt; &lt;span class="nt"&gt;-database&lt;/span&gt; home_assistant /backups/home_assistant
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;exit&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Kill and remove the intermediate container. Restart our previous docker-compose container and dbs should be restored:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker stop mystifying_kepler
&lt;span class="nv"&gt;$ &lt;/span&gt;docker-compose up &lt;span class="nt"&gt;-d&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



</description>
      <category>dockercompose</category>
      <category>docker</category>
      <category>influxdb</category>
    </item>
    <item>
      <title>Explain coroutines like I'm five</title>
      <dc:creator>Thibault Maekelbergh</dc:creator>
      <pubDate>Thu, 07 Dec 2017 10:33:37 +0000</pubDate>
      <link>https://dev.to/thibmaek/explain-coroutines-like-im-five-2d9</link>
      <guid>https://dev.to/thibmaek/explain-coroutines-like-im-five-2d9</guid>
      <description>&lt;p&gt;I have been using this concept in Javascript's redux-sagas &amp;amp; Python's asyncio but have failed to understand their underlying working. Who can explain to me how coroutines actually work?&lt;/p&gt;

</description>
      <category>explainlikeimfive</category>
      <category>discuss</category>
      <category>coroutines</category>
      <category>async</category>
    </item>
  </channel>
</rss>
