<?xml version="1.0" encoding="utf-8"?>
    <feed xmlns="http://www.w3.org/2005/Atom">
     <title>BigBinary Blog</title>
     <link href="https://www.bigbinary.com/feed.xml" rel="self"/>
     <link href="https://www.bigbinary.com/"/>
     <updated>2026-03-06T02:47:18+00:00</updated>
     <id>https://www.bigbinary.com/</id>
     <entry>
       <title><![CDATA[Running React Native dependent animations on UI thread using Reanimated]]></title>
       <author><name>Sangamesh Somawar</name></author>
      <link href="https://www.bigbinary.com/blog/running-react-native-dependent-animations-on-ui-thread-using-reanimated"/>
      <updated>2023-03-13T12:00:00+00:00</updated>
      <id>https://www.bigbinary.com/blog/running-react-native-dependent-animations-on-ui-thread-using-reanimated</id>
      <content type="html"><![CDATA[<p><img src="https://user-images.githubusercontent.com/93119254/222339128-047c3afe-543f-48a0-9420-a8c6969a156c.gif" alt="Slider"></p><p>Here we have a slider. When the user slides the slider, the loader needs to showhow much is loaded. We want to animate the loader component when the slidermoves. In other words, the loader animation is &quot;dependent&quot; on the slideranimation.</p><p>This is an example of <em>Dependent Animations</em> on the UI thread. <em>DependentAnimation</em> is when one view is animated based on another element.</p><p>first we need a gesture handler to detect the slider events. Then, based on theslider events we need to animate the progress ofthe  loader component.</p><h3>Building gesture handler to detect the slider events</h3><pre><code class="language-jsx">const [width, setWidth] = useState(200);const x = useSharedValue(0);const gestureHandler = useAnimatedGestureHandler({  onStart: (event, ctx) =&gt; {    x.value = event.absoluteX;  },  onActive: (event, ctx) =&gt; {    x.value = event.absoluteX;    // We need to calculate `progress` here based on the slider position.  },});const animatedStyle = useAnimatedStyle(() =&gt; {  return {    transform: [      {        translateX: x.value,      },    ],  };});return (  &lt;PanGestureHandler onGestureEvent={gestureHandler}&gt;    &lt;Animated.View style={[{ height: 20 }]}&gt;      &lt;Animated.View        pointerEvents=&quot;none&quot;        style={[          {            backgroundColor: &quot;blue&quot;,            height: 20,            width: 20,            borderRadius: 10,          },          animatedStyle,        ]}      /&gt;      &lt;View        pointerEvents=&quot;none&quot;        style={{          backgroundColor: &quot;black&quot;,          height: 2,          width: &quot;100%&quot;,          position: &quot;absolute&quot;,          top: 10,        }}      /&gt;    &lt;/Animated.View&gt;  &lt;/PanGestureHandler&gt;);</code></pre><p><strong>Loader component:</strong></p><pre><code class="language-jsx">&lt;View style={{ height: 20, marginTop: 10 }}&gt;  &lt;Lottie    style={{      alignSelf: &quot;center&quot;,      width: &quot;100%&quot;,    }}    progress={&quot;Calculated `progress` based on slider position.&quot;}    source={require(&quot;./progress&quot;)}    autoPlay={false}    loop={false}  /&gt;&lt;/View&gt;</code></pre><h3>Animate the loader based on the slider position</h3><p>Before we move further, let's understand the difference between UI Thread and JSThread. The UI Thread handles rendering and gestures of Android and iOS views,whereas the JS Thread takes care of all the logic of the React Nativeapplication.</p><p>&lt;video width=&quot;320&quot; height=&quot;240&quot; controls muted&gt;&lt;source src=&quot;https://user-images.githubusercontent.com/93119254/199667917-fa5e172e-6b42-481a-abb4-34040179cef7.mov&quot;&gt;&lt;/video&gt;</p><p>We have two approaches for animating the loader.</p><p>In the first approach, when the slider moves, we can store the <code>progress</code> inreact state and pass it to <a href="https://lottiefiles.com/">Lottie animation</a>. Withthis approach, the entire component rerenders on setting the <code>progress</code>.</p><pre><code class="language-jsx">const [progress, setProgress] = useState(0);const gestureHandler = useAnimatedGestureHandler({  onStart: (event, ctx) =&gt; {    x.value = event.absoluteX;  },  onActive: (event, ctx) =&gt; {    x.value = event.absoluteX;    runOnJS(setProgress)(x.value / width);  },});&lt;Lottie  style={{    alignSelf: &quot;center&quot;,    width: &quot;100%&quot;,  }}  progress={progress}  source={require(&quot;./progress&quot;)}  autoPlay={false}  loop={false}/&gt;;</code></pre><p>In the second approach, when the slider moves, we can calculate <code>progress</code> andpass it to the loader component via the<a href="https://docs.swmansion.com/react-native-reanimated/docs/api/hooks/useAnimatedProps/"><code>useAnimatedProps</code></a>.In this way, the <code>progress</code> gets calculated on the UI thread itself. Hence itavoids rerenders.</p><pre><code class="language-jsx">const LottieAnimated = Animated.createAnimatedComponent(Lottie);const [progress, setProgress] = useState(0);const lottieAnimatedProps = useAnimatedProps(() =&gt; progress: x.value / width);const gestureHandler = useAnimatedGestureHandler({  onStart: (event, ctx) =&gt; {    x.value = event.absoluteX;  },  onActive: (event, ctx) =&gt; {    x.value = event.absoluteX;  },});&lt;LottieAnimated  style={{    alignSelf: &quot;center&quot;,    width: &quot;100%&quot;,  }}  animatedProps={lottieAnimatedProps}  source={require(&quot;./progress&quot;)}  autoPlay={false}  loop={false}/&gt;;</code></pre><h3>Conclusion</h3><p>With the first approach, whenever the slider moves, the UI thread will pass thegesture event to the JS thread to store the <code>progress</code> value in react state, andwhen the <code>progress</code> value changes in the JS thread, it causes a re-render. Thisapproach creates a lot of traffic over<a href="https://reactnative.dev/architecture/threading-model">Communication Bridge</a>because of the message exchange between UI and JS threads.</p><p>So we should prefer the second approach to run any calculations on the UI threadinstead of the JS thread. Here is the entire code for reference:</p><pre><code class="language-jsx">let approach1ReRenderCount1 = 0;const Approach1: () =&gt; Node = () =&gt; {  const [width, setWidth] = useState(1);  const x = useSharedValue(0);  const [progress, setProgress] = useState(0);  const gestureHandler = useAnimatedGestureHandler({    onStart: (event, ctx) =&gt; {      x.value = event.absoluteX;    },    onActive: (event, ctx) =&gt; {      x.value = event.absoluteX;      runOnJS(setProgress)(x.value / width);    },  });  const animatedStyle = useAnimatedStyle(() =&gt; {    return {      transform: [        {          translateX: x.value,        },      ],    };  });  return (    &lt;SafeAreaView&gt;      &lt;View        style={{          height: 150,          paddingHorizontal: 20,          borderWidth: 1,          margin: 10,          justifyContent: &quot;center&quot;,        }}        onLayout={({          nativeEvent: {            layout: { width },          },        }) =&gt; {          setWidth(width);        }}      &gt;        &lt;Text style={{ fontSize: 20, paddingBottom: 10 }}&gt;Approach 1&lt;/Text&gt;        &lt;Text style={{ fontSize: 20 }}&gt;          Rerender Count : {approach1ReRenderCount1++}        &lt;/Text&gt;        &lt;View style={{ height: 20, marginTop: 10 }}&gt;          &lt;LottieAnimated            style={{              alignSelf: &quot;center&quot;,              width: &quot;100%&quot;,            }}            progress={progress}            source={require(&quot;./progress&quot;)}            autoPlay={false}            loop={false}          /&gt;        &lt;/View&gt;        &lt;PanGestureHandler onGestureEvent={gestureHandler}&gt;          &lt;Animated.View style={[{ height: 20 }]}&gt;            &lt;Animated.View              style={[                {                  backgroundColor: &quot;blue&quot;,                  height: 20,                  width: 20,                  borderRadius: 10,                },                animatedStyle,              ]}            /&gt;            &lt;View              style={{                backgroundColor: &quot;black&quot;,                height: 2,                width: &quot;100%&quot;,                position: &quot;absolute&quot;,                top: 10,              }}            /&gt;          &lt;/Animated.View&gt;        &lt;/PanGestureHandler&gt;      &lt;/View&gt;    &lt;/SafeAreaView&gt;  );};const LottieAnimated = Animated.createAnimatedComponent(Lottie);let approach2ReRenderCount = 0;const Approach2: () =&gt; Node = () =&gt; {  const [width, setWidth] = useState(1);  const x = useSharedValue(0);  const lottieAnimatedProps = useAnimatedProps(() =&gt; progress: x.value / width);  const gestureHandler = useAnimatedGestureHandler({    onStart: (event, ctx) =&gt; {      x.value = event.absoluteX;    },    onActive: (event, ctx) =&gt; {      x.value = event.absoluteX;    },  });  const animatedStyle = useAnimatedStyle(() =&gt; {    return {      transform: [        {          translateX: x.value,        },      ],    };  });  return (    &lt;SafeAreaView&gt;      &lt;View        style={{          height: 150,          paddingHorizontal: 20,          borderWidth: 1,          margin: 10,          justifyContent: &quot;center&quot;,        }}        onLayout={({          nativeEvent: {            layout: { width },          },        }) =&gt; {          setWidth(width);        }}      &gt;        &lt;Text style={{ fontSize: 20, paddingBottom: 10 }}&gt; Approach 2&lt;/Text&gt;        &lt;Text style={{ fontSize: 20 }}&gt;          Rerender Count : {approach2ReRenderCount++}        &lt;/Text&gt;        &lt;View style={{ height: 20, marginTop: 10 }}&gt;          &lt;LottieAnimated            animatedProps={lottieAnimatedProps}            style={{              alignSelf: &quot;center&quot;,              width: &quot;100%&quot;,            }}            source={require(&quot;./progress&quot;)}            autoPlay={false}            loop={false}          /&gt;        &lt;/View&gt;        &lt;PanGestureHandler onGestureEvent={gestureHandler}&gt;          &lt;Animated.View style={[{ height: 20 }]}&gt;            &lt;Animated.View              pointerEvents=&quot;none&quot;              style={[                {                  backgroundColor: &quot;blue&quot;,                  height: 20,                  width: 20,                  borderRadius: 10,                },                animatedStyle,              ]}            /&gt;            &lt;View              pointerEvents=&quot;none&quot;              style={{                backgroundColor: &quot;black&quot;,                height: 2,                width: &quot;100%&quot;,                position: &quot;absolute&quot;,                top: 10,              }}            /&gt;          &lt;/Animated.View&gt;        &lt;/PanGestureHandler&gt;      &lt;/View&gt;    &lt;/SafeAreaView&gt;  );};</code></pre>]]></content>
    </entry><entry>
       <title><![CDATA[How to remove the white screen just before the splash screen in Android]]></title>
       <author><name>Kamolesh Mondal</name></author>
      <link href="https://www.bigbinary.com/blog/adding-splash-screen-in-react-native-cli-app"/>
      <updated>2022-07-28T12:00:00+00:00</updated>
      <id>https://www.bigbinary.com/blog/adding-splash-screen-in-react-native-cli-app</id>
      <content type="html"><![CDATA[<p>In order to add a splash screen, we'll use the<a href="https://www.npmjs.com/package/react-native-splash-screen">react-native-splash-screen</a>package. While most of the job is done by following the installation steps,there are some additional steps we need to follow for Android.</p><p>There's a concept known as the &quot;preview window&quot; in Android which serves a basicpurpose of faking a fast launch of an app when the app icon is clicked. Whilepreview window fakes fast launching, it shows an empty white screen until theapp has loaded. More info available in<a href="https://www.tothenew.com/blog/disabling-the-preview-or-start-window-in-android/">this</a>article.</p><p>The preview window can itself be disabled by adding the following line in the<code>android/app/src/main/res/values/styles.xml</code> file.</p><pre><code class="language-xml">&lt;item name=&quot;android:windowDisablePreview&quot;&gt;true&lt;/item&gt;</code></pre><p>However, disabling the preview window introduces an undesirable delay betweenclicking on the app icon and the actual launching of the app.</p><p>We can get rid of the delay and the empty white screen by adding an additionalsplash activity.</p><h2>Additional steps for Android</h2><ul><li><p>Create a background_splash.xml file with the same design you used in<code>launch_screen.xml</code> during the installation above.</p></li><li><p>Place this new xml file inside the <code>android/app/src/main/res/drawable</code>directory.</p></li><li><p>Create a new splash theme in the <code>android/app/src/main/res/values/styles.xml</code>file by adding the following snippet.</p><pre><code class="language-xml">&lt;style name=&quot;SplashTheme&quot; parent=&quot;Theme.AppCompat.Light.NoActionBar&quot;&gt;    &lt;item name=&quot;android:windowBackground&quot;&gt;@drawable/background_splash&lt;/item&gt;&lt;/style&gt;</code></pre></li><li><p>Create a new splash activity to call the main activity, and add the followingcode in it.</p><pre><code class="language-java">package com.example;import android.content.Intent;import android.os.Bundle;import androidx.appcompat.app.AppCompatActivity;public class SplashActivity extends AppCompatActivity {    @Override    protected void onCreate(Bundle savedInstanceState){        super.onCreate(savedInstanceState);        Intent intent = new Intent(this, MainActivity.class);        startActivity(intent);        finish();    }}</code></pre></li><li><p>Finally, we will call this activity first on launch with the splash theme wecreated above.</p></li><li><p>Add the new activity in AndroidManifest.xml.</p><pre><code class="language-xml">&lt;activity android:name=&quot;.SplashActivity&quot; android:label=&quot;@string/app_name&quot; android:launchMode=&quot;singleTask&quot; android:theme=&quot;@style/SplashTheme&quot;&gt;  &lt;intent-filter&gt;    &lt;action android:name=&quot;android.intent.action.MAIN&quot; /&gt;    &lt;category android:name=&quot;android.intent.category.LAUNCHER&quot; /&gt;  &lt;/intent-filter&gt;&lt;/activity&gt;</code></pre></li><li><p>The intent-filter tag, with its &quot;MAIN&quot; action and &quot;LAUNCHER&quot; categorychildren, allows us to call this new activity first on launch. It's usuallyfound in the main activity by default, so we have to remove them entirely fromthere, leaving them exclusively in <code>SplashActivity</code>.</p></li></ul><p>Once we've done all this, we can rebuild the app and run it with<code>npx react-native run-android</code> to see the splashscreen we created.</p><p>The app should now launch quickly with no empty white screen on startup.</p>]]></content>
    </entry><entry>
       <title><![CDATA[Playing videos in React Native]]></title>
       <author><name>Chirag Bhaiji</name></author>
      <link href="https://www.bigbinary.com/blog/playing-videos-in-react-native-using-cloudinary"/>
      <updated>2021-09-14T12:00:00+00:00</updated>
      <id>https://www.bigbinary.com/blog/playing-videos-in-react-native-using-cloudinary</id>
      <content type="html"><![CDATA[<p>These days, a large number of mobile apps allow users to upload videos and playthose videos efficiently. While building these features in a React Native app, weran into some challenges. In this blog, we will discuss those challenges and thesolutions.</p><h2>Where to host the videos</h2><p>Since we are dealing with video,s we need a service that will store, host, encodeand be a CDN. After looking at various service providers, we decided to go with<a href="https://cloudinary.com/">Cloudinary</a>. Cloudinary is a service provider with anend-to-end image/video-management solution for web and mobile applications,covering everything from <strong><em>uploads, storage, manipulations, optimizations todelivery</em></strong> with a fast content delivery network (<strong><em>CDN</em></strong>).</p><h3>Setting up react-native-video player</h3><p>We decided to use<a href="https://github.com/react-native-video/react-native-video">react-native-video</a><code>v5.1.1</code> for playing videos in React Native application. Here is the<a href="https://github.com/react-native-video/react-native-video#readme">guide</a> to setup the video player.</p><pre><code class="language-jsx">import Video from &quot;react-native-video&quot;;const Player = ({ uri }) =&gt; {  return (    &lt;SafeAreaView style={styles.container}&gt;      &lt;Video        style={styles.player}        source={{ uri }}        controls        resizeMode=&quot;contain&quot;      /&gt;    &lt;/SafeAreaView&gt;  );};</code></pre><p>The above code snippet works perfectly on iOS but not on Android. On Android, wefaced a<a href="https://github.com/react-native-video/react-native-video/issues/1032">known issue</a>where the video doesn't play, but the audio plays with significant delay. Thisissue can be resolved by setting<a href="https://developer.android.com/guide/topics/media/exoplayer">exoplayer</a> as thedefault player for Android in <code>react-native.config.js</code> in the root directory ofthe project.</p><pre><code class="language-js">module.exports = {  dependencies: {    &quot;react-native-video&quot;: {      platforms: {        android: {          sourceDir: &quot;../node_modules/react-native-video/android-exoplayer&quot;,        },      },    },  },};</code></pre><h3>Setting up Cloudinary</h3><p>A Cloudinary account is required before proceeding. Once the account isready, following are the steps to enable unsigned upload for the account.</p><ul><li>Go to the <em>Settings</em> of Cloudinary.</li><li>Select the <em>Upload</em> tab.</li><li>Search for <em>Upload presets</em> section.</li><li>Click on <em>Enable unsigned uploading</em>.</li></ul><p>This generates an upload preset with a random name, which will be required forthe unsigned upload.</p><h2>Setting up Client for Upload</h2><h3>Selecting Video from the gallery</h3><p>We decided to use<a href="https://github.com/ivpusic/react-native-image-crop-picker">react-native-image-crop-picker</a><code>v0.36.2</code> library to select the video from the gallery. Here is the<a href="https://github.com/ivpusic/react-native-image-crop-picker#readme">guide</a> forsetting it up.</p><pre><code class="language-jsx">import ImagePicker from &quot;react-native-image-crop-picker&quot;;const selectVideo = ({ setVideoToUpload }) =&gt; {  ImagePicker.openPicker({ mediaType: &quot;video&quot; })    .then(setVideoToUpload)    .catch(console.error);};</code></pre><h3>Uploading Video</h3><pre><code class="language-js">// Cloud Name: Found on the Dashboard of Cloudinary.const URL = &quot;https://api.cloudinary.com/v1_1/&lt;CLOUD_NAME&gt;/video/upload&quot;;// Random Name: Generated After Enabling The Unsigned Uploading.const UPLOAD_PRESET = &quot;&lt;UPLOAD_PRESET_FOR_UNSIGNED_UPLOAD&gt;&quot;;const uploadVideo = (fileInfo, onSuccess, onError) =&gt; {  const { name, uri, type } = fileInfo;  let formData = new FormData();  if (uri) {    formData.append(&quot;file&quot;, { name, uri, type });    formData.append(&quot;upload_preset&quot;, UPLOAD_PRESET);  }  axios    .post(URL, formData, {      headers: { &quot;Content-Type&quot;: &quot;multipart/form-data&quot; },    })    .then(res =&gt; onSuccess(res.data))    .catch(error =&gt; onError(error));};export default { uploadVideo };</code></pre><h3>Fetching Videos on client</h3><pre><code class="language-js">import base64 from &quot;base-64&quot;;// API Key and Secret: Found on the Dashboard of Cloudinary.const API_KEY = &quot;&lt;API_KEY&gt;&quot;;const API_SECRET = &quot;&lt;API_SECRET&gt;&quot;;const URL = `https://api.cloudinary.com/v1_1/&lt;CLOUD_NAME&gt;/resources/video`;const getVideos = (onRes, onError) =&gt; {  axios    .get(URL, {      headers: {        Authorization: base64.encode(`${API_KEY}:${API_SECRET}`),      },    })    .then(res =&gt; onRes(res.data.resources))    .catch(error =&gt; onError(error));};export default { getVideos };</code></pre><p>Response:</p><pre><code class="language-json">{  &quot;resources&quot;: [    {      &quot;asset_id&quot;: &quot;475675ddd87cb3bb380415736ed1e3dc&quot;,      &quot;public_id&quot;: &quot;samples/elephants&quot;,      &quot;format&quot;: &quot;mp4&quot;,      &quot;version&quot;: 1628233788,      &quot;resource_type&quot;: &quot;video&quot;,      &quot;type&quot;: &quot;upload&quot;,      &quot;created_at&quot;: &quot;2021-08-06T07:09:48Z&quot;,      &quot;bytes&quot;: 38855178,      &quot;width&quot;: 1920,      &quot;height&quot;: 1080,      &quot;access_mode&quot;: &quot;public&quot;,      &quot;url&quot;: &quot;http://res.cloudinary.com/do77lourv/video/upload/v1628233788/samples/elephants.mp4&quot;,      &quot;secure_url&quot;: &quot;https://res.cloudinary.com/do77lourv/video/upload/v1628233788/samples/elephants.mp4&quot;    }    //...  ]}</code></pre><h3>Transforming uploaded videos for faster delivery</h3><p>One way to play a video is to first completely download the video on theclient's device and then play it locally. The biggest drawback of this approachis that it could take a long time before the video is fully downloaded and tillthen the device is not playing anything at all. This strategy also requires asophisticated algorithm to manage memory.</p><p>Another solution is to use<a href="https://en.wikipedia.org/wiki/Adaptive_bitrate_streaming">Adaptive Bitrate Streaming</a>(ABS)for playing videos. As the name suggests in this case, the video is beingstreamed. It is one of the most efficient ways to deliver videos based on theclient's internet bandwidth and device capabilities.</p><p>To generate ABS, we add an eager transformation to the upload preset we createdwhile setting up the Cloudinary account earlier.</p><p>An eager transformation runs automatically for all the videos uploaded using theupload preset which has the transformation added.</p><h3>Setup Transformation</h3><p>Here are the steps for adding an eager transformation to the upload preset.</p><ul><li>Go to the upload preset that was generated when the unsigned upload wasenabled while setting up Cloudinary in the earlier section.</li><li>Click on edit.</li><li>Go to the Upload Manipulations section.</li><li>Click on <code>Add Eager Transformation</code> to open the Edit Transaction window.</li><li>Open the <code>Format</code> dropdown under the <code>Format &amp; shape</code> section and select<code>M3U8 Playlist (HLS)</code>.</li><li>Select any streaming profile from the dropdown under the <code>More options</code>. Anyprofile matching your maximum required quality can be selected here.</li></ul><p>Next upload a video with the same preset, trigger the transformation to generateThe <code>M3U8</code> format, which is the one we need for playing the videos as streams.</p><h3>Consuming video streams</h3><p>To play streams, the <code>type</code> property must be provided to the<code>react-native-video</code>'s source prop. In this case, <code>type</code> will be <code>m3u8</code>. Also,the URI needs to be updated with this same extension.</p><pre><code class="language-diff">- source={{ uri }}+ source={{ uri: uri.replace(`.${format}`, '.m3u8'), type: 'm3u8'}}</code></pre><h3>Conclusion</h3><p>Once all the above mentioned steps were performed, we were able to uploadand stream videos on both iOS and Android without any issues.</p>]]></content>
    </entry><entry>
       <title><![CDATA[Handling environment specific configurations in React Native]]></title>
       <author><name>Sourav Kumar</name></author>
      <link href="https://www.bigbinary.com/blog/handling-environment-specific-configurations-in-react-native"/>
      <updated>2021-04-27T12:00:00+00:00</updated>
      <id>https://www.bigbinary.com/blog/handling-environment-specific-configurations-in-react-native</id>
      <content type="html"><![CDATA[<p>Many modern-day applications now go through different stages of the productcycle such as development, staging, production etc.</p><p>Having different environment variables for each environment will make it a loteasier to manage any application. This article is intended to share one solutionto address this problem in React Native. We will be using a library called<a href="https://github.com/luggit/react-native-config">react-native-config</a> for thispurpose; you can also try<a href="https://github.com/zetachang/react-native-dotenv">react-native-dotenv</a>.</p><p>We will be focusing on having three different bundles containing configurationfiles for development, staging and production environments.</p><h2>Installing react-native-config</h2><p>Install the package:</p><pre><code class="language-bash">yarn add react-native-config</code></pre><p>For iOS also run <code>pod install</code> after package is installed.</p><h2>Setup for Android</h2><p>Add below line of code to <code>android/app/build.gradle</code> to apply plugin.</p><pre><code class="language-diff">apply plugin: &quot;com.android.application&quot;+ apply from: project(':react-native-config').projectDir.getPath() + &quot;/dotenv.gradle&quot;</code></pre><p>Create three files in root folder of the project <code>.env.development</code>,<code>.env.staging</code> &amp; <code>.env.production</code> which will contain our environment variables.</p><pre><code class="language-text">// .env.developmentAPI_URL=https://myapi.development.com// .env.stagingAPI_URL=https://myapi.staging.com// .env.productionAPI_URL=https://myapi.com</code></pre><p>Now we need to define <code>envConfigFiles</code> in <code>build.gradle</code> associating builds withenv files. To achieve this, add the below code before the <code>apply from</code> call,and be sure to leave the build cases in lowercase.</p><pre><code class="language-diff">+ project.ext.envConfigFiles = [+   productiondebug: &quot;.env.production&quot;,+   productionrelease: &quot;.env.production&quot;,+   developmentrelease: &quot;.env.development&quot;,+   developmentdebug: &quot;.env.development&quot;,+   stagingrelease: &quot;.env.staging&quot;,+   stagingdebug: &quot;.env.staging&quot;+ ]apply from: project(':react-native-config').projectDir.getPath() + &quot;/dotenv.gradle&quot;</code></pre><p>Next, add <code>productFlavors</code> in <code>build.gradle</code>, just below <code>buildTypes</code>.</p><pre><code class="language-text">flavorDimensions &quot;default&quot;  productFlavors {    production {}    staging {      // We can have build-specific configurations here. Like using applicationIdSuffix to create different package name (ex. &quot;.staging&quot;)    }    development {}  }</code></pre><p>Names should match based on <code>productFlavors</code>, so <code>productiondebug</code> will match<code>debug</code> in this case and generate debug build of App with configuration from<code>.env.production</code>.</p><p>Also add <code>matchingFallbacks</code> in <code>buildTypes</code> as shown below:</p><pre><code class="language-diff">buildTypes {  debug {    signingConfig signingConfigs.debug+   matchingFallbacks = ['debug', 'release']  }  release {    signingConfig signingConfigs.debug    minifyEnabled enableProguardInReleaseBuilds    proguardFiles getDefaultProguardFil  (&quot;proguard-android.txt&quot;), &quot;proguard-rules  pro&quot;  } }</code></pre><p>Add the below scripts to <code>scripts</code> in the <code>package.json</code> file.</p><pre><code class="language-javascript">&quot;android:staging&quot;: &quot;react-native run-android --variant=stagingdebug&quot;,&quot;android:staging-release&quot;: &quot;react-native run-android --variant=stagingrelease&quot;,&quot;android:dev&quot;: &quot;react-native run-android --variant=developmentdebug&quot;,&quot;android:dev-release&quot;: &quot;react-native run-android --variant=developmentrelease&quot;,&quot;android:prod&quot;: &quot;react-native run-android --variant=productiondebug&quot;,&quot;android:prod-release&quot;: &quot;react-native run-android --variant=productionrelease&quot;,</code></pre><p>Now, to have a debug build with staging environment, run:</p><pre><code class="language-bash">yarn android:staging</code></pre><p>Or, to have a release build with staging environment, run:</p><pre><code class="language-bash">yarn android:staging-release</code></pre><h2>Setup for iOS</h2><p>In iOS we will create three new schemes <code>TestAppDevelopment</code>, <code>TestAppStaging</code> &amp;<code>TestAppProduction</code> containing configuration files for development, staging andproduction environments respectively.</p><p>To create schemes, open the project in xcode &amp; do the following:</p><p>In the Xcode menu, go to Product &gt; Scheme &gt; Edit Scheme.</p><p>Click Duplicate Scheme on the bottom and name it <code>TestAppDevelopment</code> and checkthe <code>shared</code> checkbox.</p><p>We will repeat the above steps two more times (for <code>TestAppStaging</code> &amp;<code>TestAppProduction</code>).</p><p>Now edit the newly generated scheme. Expand &quot;Build&quot; settings and click&quot;Pre-actions&quot;, and under the plus sign select &quot;New Run Script Action&quot;. Selectproject from the <code>Provide build settings from</code> dropdown.</p><p>Where it says &quot;Type a script or drag a script file&quot;, type:</p><pre><code class="language-bash">cp &quot;${PROJECT_DIR}/../.env.staging&quot; &quot;${PROJECT_DIR}/../.env&quot;  # replace .env.staging for your file</code></pre><p>Add the below scripts to <code>scripts</code> in the <code>package.json</code> file.</p><pre><code class="language-javascript">&quot;ios:dev&quot;: &quot;react-native run-ios --scheme 'TestAppDevelopment'&quot;,&quot;ios:prod&quot;: &quot;react-native run-ios --scheme 'TestAppProduction'&quot;,&quot;ios:staging&quot;: &quot;react-native run-ios --scheme 'TestAppStaging'&quot;,</code></pre><p>Now, to have a build with staging environment, run:</p><pre><code class="language-bash">yarn ios:staging</code></pre><p>And it will run the application with staging configuration.</p><h2>Accessing environment variables</h2><p>Once the setup is complete, we can access the variables as shown below:</p><pre><code class="language-javascript">import Config from &quot;react-native-config&quot;;Config.API_URL;//`https://myapi.staging.com`</code></pre>]]></content>
    </entry><entry>
       <title><![CDATA[Handling authentication state in React Native]]></title>
       <author><name>Ajay Sivan</name></author>
      <link href="https://www.bigbinary.com/blog/handling-authentication-state-in-react-native"/>
      <updated>2021-04-06T12:00:00+00:00</updated>
      <id>https://www.bigbinary.com/blog/handling-authentication-state-in-react-native</id>
      <content type="html"><![CDATA[<p>Authentication flow is an essential part of any modern mobile application. Inthis article, we will see how to store and manage the authentication state inReact Native.</p><h2>Storing auth data</h2><p>Let's consider a basic scenario where we use a phone number and authenticationtoken for verifying API requests. Here we need to persist both the phone numberand authentication token. For storing these data we can use AsyncStorage, whichis a key-value data store commonly used to persist data locally in React Native.Even though we are storing two different values, we will always access themtogether and hence we can avoid multiple AsyncStorage calls by storing them as asingle key-value pair.</p><pre><code class="language-javascript">const auth = {  phone: PHONE_NUMBER, // User's phone number  token: AUTH_TOKEN, // Authentication token};AsyncStorage.setItem(&quot;auth&quot;, JSON.stringify(auth));// AsyncStorage supports only string values - so we have to serialize our object.</code></pre><p>After storing the data we can use the <code>AsyncStorage.getItem()</code> API to retrieveit.</p><h3>Things to keep in mind while using AsyncStorage</h3><p>It should be clear from the name that AsyncStorage calls are asynchronous soconsider the following while using it.</p><ul><li><p>Avoid unnecessary AsyncStorage calls - Async calls will take time and mayaffect user experience.</p></li><li><p>Reuse data once fetched - Use higher-level state or central state managementlike Context, Redux etc to manage data used in multiple places.</p></li><li><p>Asynchronous calls might fail and we should handle exceptions properly.</p></li><li><p>In Android, AsyncStorage has a 6MB hard limit. You can change it by setting<code>AsyncStorage_db_size_in_MB</code> value in <code>gradle.properties</code> file.</p></li><li><p>AsyncStorage uses SQLite so beware of<a href="https://www.sqlite.org/limits.html">SQLite limits</a>.</p></li></ul><h2>Manage auth state globally with Context API</h2><p>In many apps we would use the current authentication state to displayappropriate content based on auth state, user type etc., so it's always good toput it in a centrally accessible place. We can use a central state managementlibrary like Redux or Context API. In this example we will use the built-inContext API.</p><p>Let's create an AuthContext and store all auth data in the Provider state byfetching it from AsyncStorage. We will also an API to update the data in state &amp;AsyncStorage.</p><h3>AuthContext</h3><pre><code class="language-javascript">// Create a contextconst AuthContext = createContext({});const AuthProvider = ({ children }) =&gt; {  const [auth, setAuthState] = useState(initialState);  // Get current auth state from AsyncStorage  const getAuthState = async () =&gt; {    try {      const authDataString = await AsyncStorage.getItem(&quot;auth&quot;);      const authData = JSON.parse(authDataString || {});      // Configure axios headers      configureAxiosHeaders(authData.token, authData.phone);      setAuthState(authData);    } catch (err) {      setAuthState({});    }  };  // Update AsyncStorage &amp; context state  const setAuth = async (auth) =&gt; {    try {      await AsyncStorage.setItem(&quot;auth&quot;, JSON.stringify(auth));      // Configure axios headers      configureAxiosHeaders(auth.token, auth.phone);      setAuthState(auth);    } catch (error) {      Promise.reject(error);    }  };  useEffect(() =&gt; {    getAuthState();  }, []);  return (    &lt;AuthContext.Provider value={{ auth, setAuth }}&gt;      {children}    &lt;/AuthContext.Provider&gt;  );};export { AuthContext, AuthProvider };</code></pre><p>When using axios we can configure the default auth headers and they will be usedfor all API requests. In case you are not using axios you can still get authvalues from the context.</p><pre><code class="language-javascript">const configureAxiosHeaders = (token, phone) =&gt; {  axios.defaults.headers[&quot;X-Auth-Token&quot;] = token;  axios.defaults.headers[&quot;X-Auth-Phone&quot;] = phone;};</code></pre><p>Now we can wrap our root component with <code>AuthProvider</code> and use the<code>useContext()</code> API in any component to access the auth state.</p><h2>Restricting routes(screens)</h2><p>Displaying appropriate screens and restricting access to screens based on theauth state is a common use case. In this section, we will see how to displayLogin/Signup screens if the user is not authenticated and a Home screen if theuser is already authenticated.</p><pre><code class="language-javascript">const App = () =&gt; {  // Get auth state from context  const { auth } = useContext(AuthContext);  return (    &lt;NavigationContainer&gt;      &lt;Stack.Navigator&gt;        {auth.token ? (          &lt;Stack.Screen name=&quot;Home&quot; component={Home} /&gt;        ) : (          &lt;&gt;            &lt;Stack.Screen name=&quot;Signup&quot; component={Signup} /&gt;            &lt;Stack.Screen name=&quot;Login&quot; component={Login} /&gt;          &lt;/&gt;        )}      &lt;/Stack.Navigator&gt;    &lt;/NavigationContainer&gt;  );};</code></pre><p>In the above snippet, we are conditionally rendering screens based on the authtoken rather than doing manual navigation using <code>navigate</code> function. This willhelp us restrict access to screens and avoid accessing restricted screens whennavigating from code or using the system back button in android.</p><h3>Things to keep in mind while using react navigation</h3><ul><li><p>Try to keep all top-level StackNavigator screens in one place to avoidunexpected behaviour.</p></li><li><p>Use conditional rendering to restrict screens.</p></li><li><p>Use <code>replace</code> instead of <code>navigate</code> when you want to remove the current screenfrom the back stack.</p></li><li><p>Use constants for screen names instead of string literals.</p></li></ul><h2>Enhancements</h2><ul><li><p>Store additional pieces of information like user type, username etc in theAuth context for easier access.</p></li><li><p>Improve AuthContext by using useReducer instead of useState.</p></li><li><p>Consider using state management libraries like Redux if you want to managelots of states globally.</p></li><li><p>Use a loader state to display a SplashScreen until we fetch the auth statefrom AsyncStorage for the first time.</p></li></ul>]]></content>
    </entry><entry>
       <title><![CDATA[Higher Order Component to render spinner React Native]]></title>
       <author><name>Chirag Shah</name></author>
      <link href="https://www.bigbinary.com/blog/higher-order-component-for-rendering-spinner-in-react-native-app"/>
      <updated>2017-11-15T12:00:00+00:00</updated>
      <id>https://www.bigbinary.com/blog/higher-order-component-for-rendering-spinner-in-react-native-app</id>
      <content type="html"><![CDATA[<p>In one of our<a href="https://bigbinary.com/blog/using-recompose-to-build-higher-order-components">previous blogs</a>,we mentioned how<a href="https://github.com/acdlite/recompose">recompose</a>improves boththe readabilityand themaintainabilityof the code.</p><p>We also sawhow <a href="https://github.com/acdlite/recompose/blob/master/docs/API.md#branch">branch</a>and<a href="https://github.com/acdlite/recompose/blob/master/docs/API.md#rendercomponent">renderComponent</a>functionsfrom <code>recompose</code>help usin decidingwhich component to renderbased on a condition.</p><p>We can use the code from<a href="https://github.com/acdlite/recompose/blob/master/docs/API.md#rendercomponent">renderComponent documentation</a>to render aspinner component whenthe data is being fetchedin a ReactJS application.</p><h2>Initial Code</h2><pre><code class="language-javascript">// PatientsList.jsimport React, { Component } from 'react';import LoadingIndicator from './LoadingIndicator';export default class PatientsList extends Component {  state = {    isLoading: true,    patientsList: [],  }  componentDidMount() {    api.getPatientsList().then(responseData =&gt; {      this.setState({        patientsList: responseData,        isLoading: false,      })    })  }  render() {    const { isLoading } = this.state;    if (isLoading) {      return &lt;LoadingIndicator isLoading={isLoading} /&gt;    } else {      return (        &lt;ScrollView&gt;          // Some header component          // View rendering the patients        &lt;/ScrollView&gt;      )    }  }</code></pre><p>In the above code,when the PatientsList component mounts,it fetchesthe list of patients from the API.During this time,the <code>isLoading</code> state is <code>true</code>,so we renderthe <code>LoadingIndicator</code> component.</p><p>Once the API callreturns with the response,we setthe <code>isLoading</code> state to <code>false</code>.This renders<code>ScrollView</code> component,with our list of patients.</p><p>The above code works fine,butif our app has multiple screens,which showthe loading indicatorand fetch data,the above wayof handling itbecomes repetitiveand hard to maintain.</p><h2>Building a higher order component</h2><p>Here's whereHigher Order Components(HOC)are very useful.We can extract the logicfor the abilityto show the loading indicatorin a HOC.</p><pre><code class="language-javascript">// withSpinner.jsimport React from &quot;react&quot;;import { ScrollView } from &quot;react-native&quot;;import LoadingIndicator from &quot;./LoadingIndicator&quot;;const withSpinner = (Comp) =&gt; ({ isLoading, children, ...props }) =&gt; {  if (isLoading) {    return &lt;LoadingIndicator isLoading={isLoading} /&gt;;  } else {    return &lt;Comp {...props}&gt;{children}&lt;/Comp&gt;;  }};export default withSpinner;</code></pre><p>Here,we created a HOC componentwhich accepts a componentand the <code>isLoading</code> prop.</p><p>If <code>isLoading</code> is true,we show the <code>LoadingIndicator</code>.If <code>isLoading</code> is false,we show the supplied componentwith its children,and pass in the props.</p><p>Now,we can use the above HOCin our PatientsList.js file.The supplied componentcan be any React Native componentbased on the use case.Here in our case,its a ScrollView.</p><pre><code class="language-javascript">// PatientsList.jsimport { ScrollView } from 'react-native';import withSpinner from './withSpinner';const ScrollViewWithSpinner = withSpinner(ScrollView);export default class PatientsList extends Component {  state = {    isLoading: true,    patientsList: [],  }  componentDidMount() {    api.getPatientsList().then(responseData =&gt; {      this.setState({        patientsList: responseData,        isLoading: false,      })    })  }  render() {    const { isLoading } = this.state;    return(      &lt;ScrollViewWithSpinner        isLoading={isLoading}        // other props      &gt;        // Some header component        // View rendering the patients      &lt;/ScrollViewWithSpinner&gt;    )  }</code></pre><h2>Conclusion</h2><p>Because of the aboveextraction of logic to a HOC,we can now use the same HOCin all our componentswhich render a loading indicatorwhile the data is being fetched.</p><p>The logicto show a loading indicatornow resides in a HOC.This makes the code easierto maintain and less repetitive.</p>]]></content>
    </entry><entry>
       <title><![CDATA[Opening non HTTPS sites in WebView in React Native]]></title>
       <author><name>Chirag Shah</name></author>
      <link href="https://www.bigbinary.com/blog/open-non-https-sites-in-webview-in-react-native"/>
      <updated>2016-07-27T12:00:00+00:00</updated>
      <id>https://www.bigbinary.com/blog/open-non-https-sites-in-webview-in-react-native</id>
      <content type="html"><![CDATA[<p>Using WebView in a React Native application allows us to reuse already built webpages.</p><p>With iOS 9 or higher if our application attempts to connect to any HTTP serverthat doesn't support the latest SSL technology<a href="http://en.wikipedia.org/wiki/Transport_Layer_Security#TLS_1.2">(TLSv1.2)</a>,WebView will fail and will not be able load the web pages.</p><p>Let's see this in action. Here we are trying to access a HTTP site via WebViewin React Native.</p><pre><code class="language-javascript">&lt;WebView style={styles.container} source={{ uri: &quot;http://del.icio.us&quot; }} /&gt;</code></pre><p>Running that on an iPhone simulator with iOS version 9.0 or greater would showfollowing error.</p><pre><code class="language-plaintext">Error Loading PageDomain: NSURLErrorDomainError Code: -1022Description: The resource could not be loaded becausethe App Transport Security policy requires the use of asecure connection</code></pre><p>Ideally, the site we are trying to connect to should have HTTPS enabled. Howeverthere might be cases where we need to connect to sites where HTTPS is notenabled.</p><p>For example, while developing the app, we might want to connect to local serverwhich is running just HTTP.</p><h2>Fix - Using Xcode</h2><p>To access HTTP sites inside our WebView, we need to open the project in Xcodeand open the <code>Info.plist</code> file.</p><p>In the list of keys, we will find <code>App Transport Security Settings</code>.</p><p>When we expand the section, we would find <code>localhost</code> inside the<code>Exception Domains</code> and the key <code>NSTemporaryExceptionAllowsInsecureHTTPLoads</code>with value <code>true</code>.</p><p>Because of this setting when we are connecting to localhost then app runs evenif server is running on HTTP and not on HTTPS.</p><p><img src="/blog_images/2016/open-non-https-sites-in-webview-in-react-native/info-plist-before.png" alt="info plist before"></p><p>So in order to make our non HTTPS site run, we need to add our website url tothis whitelisting.</p><p>When we hover over the <code>Exception Domains</code> key, we would see a <code>+</code> sign at theright hand side.</p><p>Click on it and add our domain here. Set the type to <code>dictionary</code>.</p><p>Now click on the domain we just entered and add<code>NSTemporaryExceptionAllowsInsecureHTTPLoads</code> with type <code>Boolean</code> and value<code>YES</code> similar to the one present for <code>localhost</code></p><p><img src="/blog_images/2016/open-non-https-sites-in-webview-in-react-native/info-plist-after.png" alt="info plist after"></p><p>Re-run the app using <code>react-native run-ios</code> from the terminal and now the sitewould be loaded.</p><p>If it doesn't work, then prior to running the app, do a clean build from xcode.</p><h2>Fix using any IDE</h2><p>By making the changes from XCode, if we look at the changes in info.plist file,we would find a few lines of code added.</p><p>So if we don't want to open Xcode for the fix, we can add the following linesdirectly in our info.plist.</p><p>Edit the node for the key <code>NSAppTransportSecurity</code> so that the whole node nowlooks like this:</p><pre><code class="language-plist">&lt;key&gt;NSAppTransportSecurity&lt;/key&gt;&lt;dict&gt;    &lt;key&gt;NSExceptionDomains&lt;/key&gt;    &lt;dict&gt;        &lt;key&gt;del.icio.us&lt;/key&gt;        &lt;dict&gt;            &lt;key&gt;NSTemporaryExceptionAllowsInsecureHTTPLoads&lt;/key&gt;            &lt;true/&gt;        &lt;/dict&gt;        &lt;key&gt;localhost&lt;/key&gt;        &lt;dict&gt;            &lt;key&gt;NSTemporaryExceptionAllowsInsecureHTTPLoads&lt;/key&gt;            &lt;true/&gt;        &lt;/dict&gt;    &lt;/dict&gt;&lt;/dict&gt;</code></pre><p>Be sure to re-run the app using <code>react-native run-ios</code>. Now let's see how toallow all the HTTP sites instead of whitelisting each and everyone</p><h4>Using Xcode</h4><p>Using Xcode : To allow all the non HTTPS sites, just delete the<code>Exception Domains</code> from Xcode inside info.plist and add a new key<code>Allow Arbitrary Loads</code> with the value <code>true</code>.</p><h4>Using any IDE</h4><p>Our <code>NSAppTransportSecurity</code> should just contain the following.</p><pre><code class="language-plist">&lt;key&gt;NSAppTransportSecurity&lt;/key&gt;&lt;dict&gt;    &lt;key&gt;NSAllowsArbitraryLoads&lt;/key&gt;    &lt;true/&gt;&lt;/dict&gt;</code></pre>]]></content>
    </entry><entry>
       <title><![CDATA[HTTP request headers on each WebView request]]></title>
       <author><name>Chirag Shah</name></author>
      <link href="https://www.bigbinary.com/blog/passing-request-headers-on-each-webview-request-in-react-native"/>
      <updated>2016-07-26T12:00:00+00:00</updated>
      <id>https://www.bigbinary.com/blog/passing-request-headers-on-each-webview-request-in-react-native</id>
      <content type="html"><![CDATA[<p>Using<a href="https://facebook.github.io/react-native/docs/webview.html">WebView</a>in a React Native application allowsus to reuse already built web pages.</p><p>HTTP Headers are name/value pairs thatappear in both request andresponse messages.The purpose of headers is to supplythe web server with additional informationand control how content is returned.</p><p>In React Native, while openingweb pages via WebView Component,we can pass headers to the HTTP request.Refer to our<a href="passing-user-agent-or-custom-header-in-react-native-webview">previous blog</a>for more on this.</p><pre><code class="language-javascript">&lt;WebView  source={{    uri: &quot;http://localhost:3000&quot;,    headers: { &quot;custom-app-header&quot;: &quot;react-native-ios-app&quot; },  }}/&gt;</code></pre><p>But there is a bug.On subsequent requests frominside the WebView,the headers are not passed in the request.</p><p>First, let's try to understandby recreating the bug.We have created a simple node serverwhich will act as the backendfor the application and log the requestalong with the custom header.</p><p>Here is our <code>server.js</code></p><pre><code class="language-javascript">var http = require(&quot;http&quot;);var port = 9000;function logRequest(request) {  console.log(&quot;Processing request for: &quot;, request.url);  console.log(&quot;Custom Header: &quot;, request.headers[&quot;custom-app-header&quot;]);  console.log(&quot;Request Processed\n&quot;);}http  .createServer(function (request, response) {    response.writeHead(200, { &quot;Content-Type&quot;: &quot;text/html&quot; });    switch (request.url) {      case &quot;/&quot;:        response.write(          &quot;&lt;html&gt;&lt;body&gt;Welcome&lt;a href='/bye'&gt;Bye&lt;/a&gt;&lt;/body&gt;&lt;/html&gt;&quot;        );        logRequest(request);        break;      case &quot;/bye&quot;:        response.write(&quot;&lt;html&gt;&lt;body&gt;Bye&lt;a href='/'&gt;Welcome&lt;/a&gt;&lt;/body&gt;&lt;/html&gt;&quot;);        logRequest(request);        break;      default:        break;    }    response.end();  })  .listen(port);</code></pre><p>As we can see, the <code>welcome</code> pagehas a link to <code>bye</code> and vice versa.Let's start the node server by running<code>node server.js</code>.</p><p>When we run the app on the simulator,the <code>welcome</code> page opens up,and in the server log,we can verify that the requestheader is being passed.</p><pre><code class="language-plaintext">Processing request for:  /Custom Header:  react-native-ios-appRequest Processed</code></pre><p>But when we click on the <code>Bye</code>link from the <code>Welcome</code> page,the server doesn't receive therequest header,which can be verified from the log.</p><pre><code class="language-plaintext">Processing request for:  /byeCustom Header:  undefinedRequest Processed</code></pre><p>And it can be verified againthat for any subsequent clicksthe request header doesnot get passed. We can click on<code>Welcome</code> and check the log again.</p><pre><code class="language-plaintext">Processing request for:  /Custom Header:  undefinedRequest Processed</code></pre><p>We recently encountered thisbug and created an issue<a href="https://github.com/facebook/react-native/issues/8693">here</a>.Until the issue is fixed,we have found a workaround.</p><h2>Workaround</h2><p>WebView provides a prop <code>onLoadStart</code>which accepts a function that isinvoked when the WebViewstarts loading.</p><p>We can use this prop to knowwhen a link is clickedand then re-render the WebViewcomponent with the new url.Re-rendering the WebView componentwill load the page as if it's thefirst page and then the requestheaders would be passed.</p><p>We know that in React,a component re-renders itself whenany of its state changes.The only thing which changeshere is the url, so let's move theurl to a state andinitialize it to the <code>Welcome</code> pagewhich is the root of the application.And then use the <code>onLoadStart</code> propto change the url state to the clicked url.</p><p>Here's the new code.</p><pre><code class="language-javascript">class testApp extends Component {  state = {    url: &quot;http://localhost:3000&quot;,  };  render() {    return (      &lt;WebView        onLoadStart={(navState) =&gt;          this.setState({ url: navState.nativeEvent.url })        }        source={{          uri: this.state.url,          headers: { &quot;custom-app-header&quot;: &quot;react-native-ios-app&quot; },        }}      /&gt;    );  }}</code></pre><p>Now when we run the app,we can verify in the backend thatthe request headers are being senteven when we click on <code>Bye</code> link.</p><pre><code class="language-plaintext">Processing request for:  /byeCustom Header:  undefinedRequest ProcessedProcessing request for:  /byeCustom Header:  react-native-ios-appRequest Processed</code></pre><p>One thing to note here is that,when we click on the <code>Bye</code> link,the request is not interceptedfrom reaching the server.We are just resending therequest by means of a componentre-render with the new url.</p><p>Hence in the log, we see two requests.First request took place whenuser clicked on the link,and the second request occurredwhen the component got re-renderedwith the required request headers.</p><p>This workaround might help usto pass the request headers whichwe intend to send to the backendserver until the issue gets fixed.</p>]]></content>
    </entry><entry>
       <title><![CDATA[Platform specific styles in stylesheet, React Native]]></title>
       <author><name>Bilal Budhani</name></author>
      <link href="https://www.bigbinary.com/blog/apply-platform-specific-styles-in-stylesheet-react-native"/>
      <updated>2016-07-12T12:00:00+00:00</updated>
      <id>https://www.bigbinary.com/blog/apply-platform-specific-styles-in-stylesheet-react-native</id>
      <content type="html"><![CDATA[<p>While writing cross platform applicationswe need to add platform specific styles.There are many ways of accomplishing this.</p><p>React Native<a href="https://github.com/facebook/react-native/pull/7033">introduced</a><code>Platform.select</code> helper in<a href="https://github.com/facebook/react-native/releases/tag/v0.28.0">version 0.28</a>which allows us to write platform specific styles in a concise way.In this blog we will see how to use this newly introduced feature.</p><h3>StyleSheet</h3><p>A basic stylesheet file might look something like this.</p><pre><code class="language-javascript">import { StyleSheet } from &quot;react-native&quot;;export default StyleSheet.create({  container: {    flex: 1,  },  containerIOS: {    padding: 4,    margin: 2,  },  containerAndroid: {    padding: 6,  },});</code></pre><p>Now let's see how we can re-write this StyleSheet using <code>Platform.select</code>.</p><pre><code class="language-javascript">import { StyleSheet, Platform } from &quot;react-native&quot;;export default StyleSheet.create({  container: {    flex: 1,    ...Platform.select({      ios: {        padding: 4,        margin: 2,      },      android: {        padding: 6,      },    }),  },});</code></pre>]]></content>
    </entry><entry>
       <title><![CDATA[User agent or custom header in React Native WebView]]></title>
       <author><name>Neeraj Singh</name></author>
      <link href="https://www.bigbinary.com/blog/passing-user-agent-or-custom-header-in-react-native-webview"/>
      <updated>2016-07-10T12:00:00+00:00</updated>
      <id>https://www.bigbinary.com/blog/passing-user-agent-or-custom-header-in-react-native-webview</id>
      <content type="html"><![CDATA[<p>Using <a href="https://facebook.github.io/react-native/docs/webview.html">WebView</a> in aReact Native application allows us to reuse already built web pages.</p><p>We have seen that in most of the &quot;web view&quot; based applications the links inheader are mostly turned in native navigational components. It means servershould not be sending header if React Native component is asking for web pages.However those headers should be present if the request is not coming from theReact Native app.</p><h2>Passing custom request headers</h2><p>We can configure React Native app to pass custom request headers when request ismade to the server to fetch pages.</p><pre><code class="language-javascript">let customHeaders = {  &quot;X-DemoApp-Version&quot;: &quot;1.1&quot;,  &quot;X-DemoApp-Type&quot;: &quot;demo-app-react-native&quot;,};</code></pre><p>While invoking WebView component we can pass <code>customHeaders</code> as shown below.</p><pre><code class="language-javascript">renderWebView() {  return (    &lt;WebView      source={ {uri: this.props.url, headers: customHeaders} }    /&gt;  )}</code></pre><h2>Passing user agent</h2><p>React Native also allows us to pass &quot;userAgent&quot; as a prop. However it is onlysupported by android version of React Native.</p><pre><code class="language-javascript">renderWebView() {  return (    &lt;WebView      source={ {uri: this.props.url} }      userAgent=&quot;demo-react-native-app&quot;    /&gt;  )}</code></pre><p>For iOS, we would need to add the following lines to our AppDelegate.m to setthe userAgent.</p><pre><code class="language-objectivec">NSString *newAgent = @&quot;demo-react-native-app&quot;;NSDictionary *dictionary = [[NSDictionary alloc] initWithObjectsAndKeys:newAgent, @&quot;UserAgent&quot;, nil];[[NSUserDefaults standardUserDefaults] registerDefaults:dictionary];</code></pre>]]></content>
    </entry><entry>
       <title><![CDATA[Data exchange between React Native app and WebView]]></title>
       <author><name>Bilal Budhani</name></author>
      <link href="https://www.bigbinary.com/blog/send-receive-data-between-react-native-and-webview"/>
      <updated>2016-05-25T12:00:00+00:00</updated>
      <id>https://www.bigbinary.com/blog/send-receive-data-between-react-native-and-webview</id>
      <content type="html"><![CDATA[<p>A project we recently worked on needed some complicated charts.We builtthose charts using JavaScript library and it worked fine on browsers.</p><p>Now we need to build mobile app using<a href="https://facebook.github.io/react-native/">React Native</a>and it would take alot of time to build those charts natively. So we decided to useWebView (Link is not available )to render the html pages which already displays charts nicely.</p><p>React Native comes with <code>WebView</code>component by default.So rendering the html page using <code>WebView</code> was easy.However, once the page is rendered the React Native app could not exchangeany data with the web page.</p><p>In this blog post we'll discuss how tomake React Native app communicate with the pages rendered using<code>WebView</code>with the help of<a href="https://github.com/alinz/react-native-webview-bridge">react-native-webview-bridge</a>library.</p><h2>What is React Native WebView Bridge ?</h2><p><code>react-native-webview-bridge</code>isa wrapper on top of React Native's <code>WebView</code> component with some extra features.</p><p>First we need to install<a href="https://github.com/alinz/react-native-webview-bridge">react-native-webview-bridge</a>package.</p><pre><code class="language-javascript">npm install react-native-webview-bridge --save</code></pre><p>Next we need to import the <code>WebView</code> bridge module.</p><pre><code class="language-javascript">// ES6import WebViewBridge from &quot;react-native-webview-bridge&quot;;// ES5let WebViewBridge = require(&quot;react-native-webview-bridge&quot;);</code></pre><p>Now let's create a basic React component.This component will be responsible for rendering html page using<code>WebView</code>.</p><pre><code class="language-javascript">React.createClass({  render: function () {    return (      &lt;WebViewBridge        ref=&quot;webviewbridge&quot;        onBridgeMessage={this.onBridgeMessage.bind(this)}        source={{ uri: &quot;https://www.example.com/charts&quot; }}      /&gt;    );  },});</code></pre><p>After the component is mounted then we will send data to web view.</p><pre><code class="language-javascript">componentDidMount() {  let chartData = {data: '...'};  // Send this chart data over to web view after 5 seconds.  setTimeout(() =&gt; {    this.refs.webviewbridge.sendToBridge(JSON.stringify(data));  }, 5000);},</code></pre><p>Next, We will add code to receive data from web view.</p><pre><code class="language-javascript">onBridgeMessage: function (webViewData) {  let jsonData = JSON.parse(webViewData);  if (jsonData.success) {    Alert.alert(jsonData.message);  }  console.log('data received', webViewData, jsonData);  //.. do some react native stuff when data is received}</code></pre><p>At this time code should look something like this.</p><pre><code class="language-javascript">React.createClass({  componentDidMount() {    let chartData = { data: &quot;...&quot; };    // Send this chart data over to web view after 5 seconds.    setTimeout(() =&gt; {      this.refs.webviewbridge.sendToBridge(JSON.stringify(data));    }, 5000);  },  render: function () {    return (      &lt;WebViewBridge        ref=&quot;webviewbridge&quot;        onBridgeMessage={this.onBridgeMessage.bind(this)}        source={{          uri: &quot;https://www.example.com/charts&quot;,        }}      /&gt;    );  },  onBridgeMessage: function (webViewData) {    let jsonData = JSON.parse(webViewData);    if (jsonData.success) {      Alert.alert(jsonData.message);    }    console.log(&quot;data received&quot;, webViewData, jsonData);    //.. do some react native stuff when data is received  },});</code></pre><p>Okay, We've added all the React Native side of code.We now need to add some JavaScript code on our web page to complete the functionality.</p><h2>Why do we need to add JavaScript snippet on my web page?</h2><p>This is a two way data exchange scenario. When our React Native app sends any data, this JavaScript snippet will parse that data and will trigger functions accordingly. We'll also be able to send some data back to React Native app from JavaScript.</p><p>The<a href="https://github.com/alinz/react-native-webview-bridge#simple-example">example in the README</a>of WebViewBridge library shows how to inject JavaScript snippet in React component.However, we prefer JavaScript code to be added to web page directlysince it provides more control and flexibility.</p><p>Coming back to our implementation, Let's now add the snippet in our web page.</p><pre><code class="language-javascript">&lt;script&gt; (function () {    if (WebViewBridge) {      // This function gets triggered when data received from React Native app.      WebViewBridge.onMessage = function (reactNativeData) {        // Converts the payload in JSON format.        var jsonData = JSON.parse(reactNativeData);        // Passes data to charts for rendering        renderChart(jsonData.data);        // Data to send from web view to React Native app.        var dataToSend = JSON.stringify({success: true, message: 'Data received'});        // Keep calm and send the data.        WebViewBridge.send(dataToSend);      };    }  }())&lt;/script&gt;</code></pre><p>Done! We've achieved our goal of having a two way communication channel between our React Native app and the web page.</p><p>Checkout<a href="https://github.com/alinz/react-native-webview-bridge/tree/master/examples/SampleRN20">this link</a>for more examples of how to use WebView Bridge.</p>]]></content>
    </entry><entry>
       <title><![CDATA[Using Image as a Container in React Native]]></title>
       <author><name>Bilal Budhani</name></author>
      <link href="https://www.bigbinary.com/blog/using-image-as-a-container-in-react-native"/>
      <updated>2016-04-28T12:00:00+00:00</updated>
      <id>https://www.bigbinary.com/blog/using-image-as-a-container-in-react-native</id>
      <content type="html"><![CDATA[<p>Adding a nice looking background to a screen makes an app visually appealing. Italso makes the app look more sleek and elegant. Let us see how we can leveragethis technique in React Native and add an image as a background.</p><p>Well need to create different sizes of the background image, which were goingto use as a container. React Native will pick up appropriate image based on thedevices dimension (check<a href="http://facebook.github.io/react-native/docs/images.html#content">Images guide</a>for more information).</p><pre><code class="language-plaintext">- login-background.png (375x667)- login-background@2x.png (750x1134)- login-background@3x.png (1125x2001)</code></pre><p>Now well use these images in our code as container.</p><pre><code class="language-javascript">//...render() {    return (      &lt;Image        source={require('./images/login-background.png')}        style={styles.container}&gt;        &lt;Text style={styles.welcome}&gt;          Welcome to React Native!        &lt;/Text&gt;        &lt;Text style={styles.instructions}&gt;          To get started, edit index.ios.js        &lt;/Text&gt;        &lt;Text style={styles.instructions}&gt;          Press Cmd+R to reload,{'\n'}          Cmd+D or shake for dev menu        &lt;/Text&gt;      &lt;/Image&gt;    );  }//...const styles = StyleSheet.create({  container: {    flex: 1,    width: undefined,    height: undefined,    backgroundColor:'transparent',    justifyContent: 'center',    alignItems: 'center',  },});</code></pre><p>Weve intentionally left height and width of the image as <code>undefined</code>. This willlet React Native take the size of the image from the image itself. This way, wecan use Image component as View and add other components as a children to buildUI.</p><p><img src="/blog_images/2016/using-image-as-a-container-in-react-native/image-as-container-react-native.png" alt="image as container in react native"></p>]]></content>
    </entry><entry>
       <title><![CDATA[Using Stripe API in React Native with fetch]]></title>
       <author><name>Chirag Shah</name></author>
      <link href="https://www.bigbinary.com/blog/using-stripe-api-in-react-native-with-fetch"/>
      <updated>2015-11-03T12:00:00+00:00</updated>
      <id>https://www.bigbinary.com/blog/using-stripe-api-in-react-native-with-fetch</id>
      <content type="html"><![CDATA[<p><a href="https://stripe.com">Stripe</a> makes it easy to accept payments online. Stripeoffers an <a href="https://www.npmjs.com/package/stripe">npm package</a> with<a href="https://stripe.com/docs/api/node">nice documentation</a>.</p><h2>Problem with using Stripe in React Native</h2><p>I added stripe to my React Native application using following command.</p><pre><code class="language-plaintext">npm install stripe --save</code></pre><p>Added Stripe to the project as shown below.</p><pre><code class="language-plaintext">var stripe = require('stripe')('stripe API key');</code></pre><p>And now I'm getting following error.</p><p><img src="/blog_images/2015/using-stripe-api-in-react-native-with-fetch/http_error_in_react_native.png" alt="could not mount"></p><p>This's because npm package uses <a href="https://stripe.com/docs/stripe.js">stripe.js</a>and stripe.js needs <a href="https://nodejs.org/api/http.html">http</a> provided byNode.JS.</p><p>Our React Native code does not actually run on Node.JS. I know while buildingReact Native we use NPM and we have a node server running in the background soit feels like we are in Node.JS land and we can use everything Node.JS has tooffer. But that's not true. React Native code can't depend on Node.JS becauseNode.JS is not shipped with ios. It means we can't use any of Node.JS packagesincluding <code>http</code>. It means we can't use <code>stripe</code> npm module.</p><p>Now you can remove <code>require('stripe')</code> line that was added in the last step.</p><h2>Solution</h2><p>React Native comes with <a href="https://fetch.spec.whatwg.org">Fetch</a><a href="https://github.com/github/fetch">api</a>. From the documentation</p><blockquote><p>The Fetch API provides a JavaScript interface for accessing an manipulatingparts of the HTTP pipeline, such as requests and responses. It also provides aglobal fetch() method that provides an easy, logical way to fetch resourcesasynchronously across the network.</p></blockquote><p>stripe.js is a nice wrapper around stripe api. stripe api is very welldocumented and is easy to use. Since we can't use stripe.js we will use stripeapi.</p><p>We will use fetch to<a href="https://stripe.com/docs/api#create_card_token">create a credit card token using api</a>.</p><pre><code class="language-javascript">return fetch(&quot;https://api.stripe.com/v1/tokens&quot;, {  method: &quot;post&quot;,  headers: {    Accept: &quot;application/json&quot;,    &quot;Content-Type&quot;: &quot;application/x-www-form-urlencoded&quot;,    Authorization: &quot;Bearer &quot; + &quot;&lt;YOUR-STRIPE-API-KEY&gt;&quot;,  },  body: formBody,});</code></pre><p>Here, the header has 3 keys. Lets go through them one-by-one:</p><ul><li><code>'Accept': 'application/json'</code> : Designates the content to be in JSON format.</li><li><code>'Content-Type': 'application/x-www-form-urlencoded'</code> : This tells theendpoint that the payload will be one giant query string where name and valuepairs will be separated by the ampersand.</li><li><code>'Authorization': 'Bearer ' + '&lt;YOUR-STRIPE-API-KEY&gt;'</code> : This key is toauthorize our actions on Stripe. Here <code>Bearer</code> is just a prefix which we needto attach to the api-key because Stripe uses OAuth 2.0 . You can read more onthe Bearer token usage<a href="http://self-issued.info/docs/draft-ietf-oauth-v2-bearer.html">here</a> .</li></ul><p>Payload needs to contain credit card details.</p><pre><code class="language-javascript">var cardDetails = {  &quot;card[number]&quot;: &quot;1111 2222 3333 4444&quot;,  &quot;card[exp_month]&quot;: &quot;01&quot;,  &quot;card[exp_year]&quot;: &quot;2020&quot;,  &quot;card[cvc]&quot;: &quot;123&quot;,};</code></pre><p>Since the <code>Content-Type</code> is <code>application/x-www-form-urlencoded</code>, the payloadshould be one query string. An example of this would be:<code>city=Miami&amp;state=Florida</code></p><p>Let's prepare a proper payload.</p><pre><code class="language-plaintext">var formBody = [];for (var property in cardDetails) {  var encodedKey = encodeURIComponent(property);  var encodedValue = encodeURIComponent(cardDetails[property]);  formBody.push(encodedKey + &quot;=&quot; + encodedValue);}formBody = formBody.join(&quot;&amp;&quot;);</code></pre><p>That's it. Now we can attach <code>formBody</code> to the body part of the fetch requestand we are good to go.</p><h2>Final solution</h2><p>Here's the whole code snippet.</p><pre><code class="language-javascript">&quot;use strict&quot;;var stripe_url = &quot;https://api.stripe.com/v1/&quot;;var secret_key = &quot;&lt;YOUR-STRIPE-API-KEY&gt;&quot;;module.exports.createCardToken = function (cardNumber, expMonth, expYear, cvc) {  var cardDetails = {    &quot;card[number]&quot;: cardNumber,    &quot;card[exp_month]&quot;: expMonth,    &quot;card[exp_year]&quot;: expYear,    &quot;card[cvc]&quot;: cvc,  };  var formBody = [];  for (var property in cardDetails) {    var encodedKey = encodeURIComponent(property);    var encodedValue = encodeURIComponent(cardDetails[property]);    formBody.push(encodedKey + &quot;=&quot; + encodedValue);  }  formBody = formBody.join(&quot;&amp;&quot;);  return fetch(stripe_url + &quot;tokens&quot;, {    method: &quot;post&quot;,    headers: {      Accept: &quot;application/json&quot;,      &quot;Content-Type&quot;: &quot;application/x-www-form-urlencoded&quot;,      Authorization: &quot;Bearer &quot; + secret_key,    },    body: formBody,  });};</code></pre><p>This is an example of registering a credit card with stripe and getting thetoken. Similar implementations can be done for other API endpoints.</p>]]></content>
    </entry><entry>
       <title><![CDATA[How to test React Native App on a real iPhone]]></title>
       <author><name>Chirag Shah</name></author>
      <link href="https://www.bigbinary.com/blog/how-to-test-react-native-app-on-real-iphone"/>
      <updated>2015-08-25T12:00:00+00:00</updated>
      <id>https://www.bigbinary.com/blog/how-to-test-react-native-app-on-real-iphone</id>
      <content type="html"><![CDATA[<p>I have been developing a new iOS app using<a href="https://facebook.github.io/react-native/">React Native</a>. I have been testing itusing simulator provided by Xcode. Now it's time to test the app using a realdevice. One could ask why test on a real device if it works perfectly on asimulator. Here are some of the reasons.</p><ul><li>There are a number of device specific features which are not testable on asimulator. Phone calls, camera, gps data, compass and vibration are a few ofthem.</li><li>Testing unexpected cases that can only be tested on a real device. Like howyour application handles incoming calls, low memory situations, low diskspace, limited data connectivity etc.</li><li>Hardware-wise, your device is different than your Mac. If your app is graphicsintensive and requires a lot of CPU usage, it might work seamlessly on yoursimulator depending on your Mac's specifications. But it might be laggy andglitchy on the real device.</li></ul><p>Now let's look into how you can start testing your React Native app on an realiPhone.</p><h2>Step 1 - Plug in your device</h2><p>Plug in your device to your Mac and open Xcode. You will be able to select yourdevice to the right of the Play and Stop buttons.</p><p><img src="/blog_images/2015/how-to-test-react-native-app-on-real-iphone/select-device.png" alt="select device"></p><p>You might run into one of the following two errors if you have not enrolled intoApple's Developer Program.</p><h2>Error 1 : Failed to code sign</h2><p><img src="/blog_images/2015/how-to-test-react-native-app-on-real-iphone/xcode-error1.png" alt="xcode error 1"></p><p><img src="/blog_images/2015/how-to-test-react-native-app-on-real-iphone/xcode-error2.png" alt="xcode error 2"></p><p>To fix above issues please enroll in Apple Developer program.</p><h2>Error 2 : Disk Image could not be mounted</h2><pre><code class="language-plaintext">The Developer Disk Image could not be mounted :You may be running a version of iOS that is not supported by this version of XCode</code></pre><p><img src="/blog_images/2015/how-to-test-react-native-app-on-real-iphone/could-not-mount.png" alt="could not mount"></p><p>This can happen if your iOS version is not supported by your current version ofXcode. To fix this, just update your Xcode to the latest version from the AppStore.</p><h2>Step 2 - Set right deployment target</h2><p>In your Xcode project setup the <code>Deployment Target</code> should be set to less thanor equal to the iOS version installed on the device.</p><p>If your iPhone is running iOS 7.0 and you have set &quot;Deployment Target&quot; as 8.0then the app <strong>will not</strong> work. If your iPhone is running iOS 8.0 and you haveset &quot;Deployment Target&quot; as 7.0 then the app <strong>will</strong> work.</p><p><img src="/blog_images/2015/how-to-test-react-native-app-on-real-iphone/deployment-target.png" alt="deployment target"></p><h2>Step 3 - Fix &quot;Could not connect to development server&quot; error</h2><p>So the app is installed and you can see the launch screen. That's great. Butsoon you might get following error on your iPhone.</p><pre><code class="language-plaintext">Could not connect to development server.Ensure node server is running and available on the samenetwork  run npm start from react-native root.</code></pre><p><img src="/blog_images/2015/how-to-test-react-native-app-on-real-iphone/connection-error.PNG" alt="connection error"></p><p>To fix this open <code>AppDelegate.m</code> file in your project's iOS folder.</p><p>Locate line mentioned below and replace <code>localhost</code> with your Mac IP addresse.g. <code>192.168.x.x</code>.</p><p>To find the ip address of your Mac this is what ReactNative suggests us to do.</p><blockquote><p>you can get ip by typing <code>ifconfig</code> into the terminal and selecting the * &gt;<code>inet</code> value under <code>en0:</code>.</p></blockquote><pre><code class="language-objectivec">jsCodeLocation = [NSURL URLWithString:@&quot;http://localhost:8081/index.ios.bundle&quot;];</code></pre><p>Save and click run again. This will fix the error.</p><h2>Step 3 - Fix connecting to API hosted on local development server</h2><p>Now the app is installed and you can navigate through the app screens. If theapp attempts to make API call to the server running locally on the machine thendepending on how the local server was started it could be a problem.</p><p>In my case, I have a Ruby on Rails application running on my local machine. Ihad started my rails server by executing command <code>rails server</code>.</p><p>To do this, the app should be able to access the rails server using the privateip of the server e.g. <code>192.168.x.x:3000</code>. Turned out that I had started my railsserver using command <code>rails server</code>.</p><p>Because of<a href="http://guides.rubyonrails.org/4_2_release_notes.html#default-host-for-rails-server">change in Rails 4.2</a>rails server listens to <code>localhost</code> and not <code>0.0.0.0</code>.</p><p>The rails guide further says following.</p><blockquote><p>with this change you will no longer be able to access the Rails server from adifferent machine, for example if your development environment is in a virtualmachine and you would like to access it from the host machine. In such cases,please start the server with rails server -b 0.0.0.0 to restore the oldbehavior.</p></blockquote><p>In this case iPhone is trying to talk to Rails server so Rails server must bestarted using following command.</p><pre><code class="language-plaintext">rails server -b 0.0.0.0orrails server --binding=0.0.0.0</code></pre><p>By doing so, now you can connect to your Rails app from your local network, bybrowsing to <code>http://192.168.x.x:3000</code>.</p><h2>Summary</h2><p>These were some of the issues I came across while testing my iOS app on anactual device and how I fixed them. Hopefully, this blog post helps you fixthese error cases get you started on testing quickly.</p>]]></content>
    </entry>
     </feed>