<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Balvinder Gambhir on Medium]]></title>
        <description><![CDATA[Stories by Balvinder Gambhir on Medium]]></description>
        <link>https://medium.com/@balvinder.gambhir?source=rss-509cf1ac9e4d------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*uJMGqTLk-PdMwR8dD3k54A@2x.jpeg</url>
            <title>Stories by Balvinder Gambhir on Medium</title>
            <link>https://medium.com/@balvinder.gambhir?source=rss-509cf1ac9e4d------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sun, 24 May 2026 03:41:30 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@balvinder.gambhir/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[Optimizing Configuration time for Android apps that use React Native]]></title>
            <link>https://bytes.swiggy.com/optimizing-configuration-time-for-android-apps-that-use-react-native-81bb8c9bffdf?source=rss-509cf1ac9e4d------2</link>
            <guid isPermaLink="false">https://medium.com/p/81bb8c9bffdf</guid>
            <category><![CDATA[hybrid-apps]]></category>
            <category><![CDATA[gradle]]></category>
            <category><![CDATA[android]]></category>
            <dc:creator><![CDATA[Balvinder Gambhir]]></dc:creator>
            <pubDate>Mon, 25 Mar 2024 01:51:38 GMT</pubDate>
            <atom:updated>2024-03-25T01:51:38.683Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Iw63cwRzvWJJVu0jyn3eVQ.jpeg" /></figure><p>Swiggy is a super app that hosts a range of business lines, including Food Delivery, Instamart, Dineout, Minis, Insanely Good, Mall, and more. Each of these business lines operates on its own distinct technology stack, with React Native being among the frameworks utilized.</p><p>There was a sharp increase in the time taken to complete the configuration phase of the build process on adding react native to our app. For configuring the project, it was taking more than <strong>1 minute</strong>. Imagine waiting at least 1 minute every time you want to see your changes 😔</p><p>To debug, let’s first understand how our app is built by the gradle build system.</p><p>The build process of gradle goes through various build phases.</p><p>The build phases of gradle are</p><p>1. Initialization phase — In this phase Gradle tries to identify all the projects involved in the build process.</p><p>2. Configuration phase — During this phase, Gradle executes the build script of each project identified in the previous phase</p><p>3. Execution Phase — This is the last phase. During this phase, Gradle identifies the tasks that need to be executed created in the previous phase, and executes them according to their dependency order. All the build work and activities are done in this phase. For example: compiling the app, generating build etc.</p><p>Now that we know the phases of the gradle build process, let’s debug this.</p><p>To debug, let’s run a gradle scan to see what is taking so much time.<br>To run a gradle scan, you can execute the below command</p><pre>./gradlew dependencies --scan</pre><p>Running this will generate a link to get the details of the tasks that were executed.</p><p>By going to the configuration tab ( you can reach it by clicking on performance from the navigation rail and then pressing on the configuration tab), it was observed that the total configuration time for our app was <strong>1 minute and 26 seconds</strong>, with 1 minute and 7 seconds (<strong>86% of the time</strong>) being spent on executing the native_modules.gradle script.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*kZCzb-W6511VGNGgnPeMbA.png" /><figcaption>Configuration Time Breakup</figcaption></figure><p>If your react native app has a dependency on <strong>react-native-community</strong>, then your app will need to apply the native_modules.gradle script. Applying it 1 time can increase the configuration time by around 30 seconds. This was applied two times (one time in settings.gradle and one time in another module), so the increase in time was doubled for us.</p><p>After going through the code written in native_modules.gradle, it was observed that 2 node commands are being executed by the script.</p><p>1.node -e &quot;try {console.log(require(&#39;@react-native-community/cli&#39;).bin);} catch (e){console.log(require(&#39;react-native/cli&#39;).bin;}” — This command resolves the command line path. This command executes very fast.</p><p>2. node $cliPath config — This command generates a JSON output which the gradle command uses to generate PackageList.java. Sample output for this command can be found here — <a href="https://gist.github.com/balvinderg/5821aa34812508fdb1499d540420c948">gist link</a></p><p>The majority of the configuration time is spent in running the node config command. <br>To reduce the configuration phase time, we decided to modify the native_module’s gradle script and cache the output of the config command as it is same for every project configuration given that the package.json and its dependencies have not changed.</p><p>This is how the existing script works</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/454/1*lLQ8k_rIYVAfWI9Ap2w3gA.png" /><figcaption>Flow diagram for existing native_modules.gradle</figcaption></figure><p>and this will how the modified native_modules.gradle will work</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*W6AWBgxZIa0dP-TTipTokQ.png" /><figcaption>Flow diagram for modified native_modules.gradle</figcaption></figure><p><strong>Explanation</strong></p><ol><li>We are going to cache the output of the node config command and save the output of it in the node_command_output.json file. We will also save the current package.json in cached_package.json and the current package-lock.json in cached_package-lock.json so that we can compare them later while deciding whether to use cached_output or not.</li><li>We are replacing the output of the node config command by replacing the result of root.absolutePath with “to_be_replaced_with” so that the output remains the same for any developer working on the project. We will replace “to_be_replaced_with” back to the root.absolutePath when we are going to use the cache.</li></ol><p>The modified native_module.gradle script can be downloaded from here — <a href="https://gist.github.com/balvinderg/d7b93e329c84ae81eab7936f9c9b8f54">https://gist.github.com/balvinderg/d7b93e329c84ae81eab7936f9c9b8f54</a></p><p>If you are on the latest version of React Native community , you can use this — <a href="https://gist.github.com/balvinderg/d6fafbb75c06e4388cf51ac3d1bf41a1">https://gist.github.com/balvinderg/d6fafbb75c06e4388cf51ac3d1bf41a1</a></p><p>To make sure that all the devs use the same native_modules.gradle script, you can use patch-package.</p><p>To add patch-package, you can add patch-package as a devDependencies in package.json and a script to run patch-package when node modules are installed.</p><pre>{<br>  &quot;name&quot;: &quot;example&quot;,<br>  &quot;private&quot;: true,<br>  &quot;version&quot;: &quot;0.0.0&quot;,<br>  &quot;description&quot;: &quot;&quot;,<br>  &quot;scripts&quot;: { //Add this in your package.json<br>    &quot;postinstall&quot;: &quot;patch-package&quot; //Add this in your package.json<br>  },<br>  &quot;devDependencies&quot;: { <br>    &quot;patch-package&quot;: &quot;6.4.7&quot; //Add this in your package.json<br>  }<br>}</pre><p>Now run command</p><pre>npx patch-package @react-native-community/cli-platform-android</pre><p>after modifying the native_modules.gradle script.<br>This will ensure all devs are using the same native_modules.gradle script while running the builds.</p><p><strong>Results</strong></p><p>After modifying the native_modules.gradle script, our configuration time decreased from 1m 26s to<strong> 12s</strong> i.e an <strong>86% </strong>decrease in configuration time 🚀🚀</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*K4DivvF5wOXcpM74LTqmXw.png" /><figcaption>Configuration time breakup (after)</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*rsKKVTq5P51yvNAOWYdIiQ.png" /><figcaption>New execution time for native_modules.gradle</figcaption></figure><p><strong>Savings Per Day</strong></p><p>Average build generation per day in Android team— 55<br>Average Savings per build — 74 seconds<br>Total Savings — 55 * 74 = 4070 seconds ~= <strong>1 hour per day</strong></p><h4>Resources</h4><ol><li><a href="https://www.npmjs.com/package/patch-package">https://www.npmjs.com/package/patch-package</a></li><li><a href="https://proandroiddev.com/understanding-gradle-the-build-lifecycle-5118c1da613f">https://proandroiddev.com/understanding-gradle-the-build-lifecycle-5118c1da613f</a></li></ol><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=81bb8c9bffdf" width="1" height="1" alt=""><hr><p><a href="https://bytes.swiggy.com/optimizing-configuration-time-for-android-apps-that-use-react-native-81bb8c9bffdf">Optimizing Configuration time for Android apps that use React Native</a> was originally published in <a href="https://bytes.swiggy.com">Swiggy Bytes — Tech Blog</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Building the WhatToEat Experience on Android]]></title>
            <link>https://bytes.swiggy.com/building-the-whattoeat-experience-on-android-8ecf7d010ca8?source=rss-509cf1ac9e4d------2</link>
            <guid isPermaLink="false">https://medium.com/p/8ecf7d010ca8</guid>
            <category><![CDATA[jetpack-compose]]></category>
            <category><![CDATA[custom-layout]]></category>
            <category><![CDATA[swiggy-mobile]]></category>
            <category><![CDATA[user-experience]]></category>
            <category><![CDATA[swiggy-engineering]]></category>
            <dc:creator><![CDATA[Balvinder Gambhir]]></dc:creator>
            <pubDate>Fri, 01 Sep 2023 08:22:25 GMT</pubDate>
            <atom:updated>2023-09-01T08:22:25.561Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/600/1*tCeBhNn86N2S-xfvEsPC9g.gif" /></figure><h4>Part 1 — Creating the Bubble’s Layout</h4><p><strong>What to Eat?</strong></p><p>It’s a question I have found myself pondering a lot of times. Well, I don’t have to anymore. Why?</p><p>Introducing Swiggy’s WhatToEat, a feature that enhances the traditional food-ordering journey by adding the crucial element of discovering food through emotions.</p><p><strong>Goal</strong></p><p>The goal of “<strong>WhatToEat</strong>” is to help users overcome decision fatigue while ordering food. The feature aims to simplify the decision-making process by allowing users to indicate their current mood or specific cuisine preferences through bubbles.</p><p>In this multi-part series, I will explain how we used<strong> Jetpack Compose</strong> to create the <strong>WhatToEat</strong> feature.</p><ul><li>In the part 1 of our series, we’ll focus on recreating the eye-catching bubble layout that you see in this feature.</li><li>In the next part of the series, I will explain how we animated each of the bubbles.</li></ul><p><strong>Creating the Bubble’s Layout</strong></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/283/1*3S_48krOUrb9x7m_76GOCA.png" /><figcaption>Bubbles layout</figcaption></figure><ol><li>The layout consists of a list of bubbles.</li><li>The bubbles are following a 1–2–1–2–1… and so on pattern.</li><li>Every bubble’s position is randomly moved in both horizontal (X) and vertical (Y) directions.</li></ol><p>Our bubble’s UI is very simple. Its just a image with text underneath it. <br>In compose, this is how we implemented it.</p><pre>@Composable<br>fun Bubble(imageUrl: String,<br>           subtitle: String,<br>           modifier: Modifier = Modifier,<br>           contentDescription: String? = null) {<br>    Column(modifier) {<br>        AsyncImage(model = imageUrl, contentDescription = contentDescription)<br>        Text(text = subtitle)<br>    }<br>}</pre><p>Now let’s focus on creating the 1–2–1 pattern. <br>A naive approach to build this would be to create this layout using column with multiple rows inside it. For eg</p><pre>@Composable<br>fun NaiveLayout(bubbles: List&lt;Bubble&gt;){<br>    Column(modifier = Modifier.fillMaxWidth().padding(top = 86.dp)) {<br>        Row(modifier = Modifier.fillMaxWidth(),<br>            horizontalArrangement = Arrangement.Center) {<br>           Bubble()<br>        }<br>        Row(modifier = Modifier.fillMaxWidth(),<br>            horizontalArrangement = Arrangement.SpaceAround) {<br>            Bubble()<br>            Bubble()<br>        }<br>        Row(modifier = Modifier.fillMaxWidth(),<br>            horizontalArrangement = Arrangement.Center) {<br>            Bubble()<br>        }<br>        Row(modifier = Modifier.fillMaxWidth(),<br>            horizontalArrangement = Arrangement.SpaceAround) {<br>            Bubble()<br>            Bubble()<br>        } <br>        ... and so on<br>    }<br>}</pre><p><strong>Why is this approach naive?</strong></p><ol><li>This layout will not scale if we decide to add more patterns. We will need to add new layouts for each pattern.</li><li>This layout does not support animating the bubbles individually.</li><li>The columns and rows approach is too restrictive. We are dependent on the arrangment property for the bubble’s position. We cannot place the bubble anywhere.</li><li>Earlier we saw that each bubble was randomly moved in the X and Y direction. We cannot do this in this approach (We can do it by using translationY and padding but i feel like that would just be a hackish way to do this).</li></ol><p>So what will be a better approach to make this?</p><p>The answer is <strong>Custom Layout.</strong></p><p>Custom layout gives us the capability to control each composable’s position on a pixel level and is also very simple to use.</p><p>To create a custom layout, compose has a composable called <strong>Layout.</strong></p><p>Layout composable accepts a list of composable and a measurePolicy(this is a way to measure and position the composables).</p><p>Before going to the layout’s code, let’s create an interface for placing the bubbles.<br>The interface will have 2 methods</p><ol><li>place — This method will return us the x and y coordinates for the bubble at position index.</li><li>getLayoutHeight — This will accept the number of bubbles, the bubble’s height and return us the total height the bubbles will take.</li></ol><pre>interface BubblePlacer{<br><br>    /** This function calculates the x and y poisiton for the bubble at any index.<br>     *  [maxWidth] and [widthOfBubble] is used to help decide the x position of the bubble<br>     *  [density] is used to convert dp to px<br>     *  [heightOfBubble] is used to calculate the y position<br>     *  [topPadding] is the padding from top of the screen<br>     */<br>    fun place(<br>        index: Int,<br>        maxWidth: Int,<br>        heightOfBubble: Int,<br>        widthOfBubble: Int,<br>        topPadding: Int,<br>        density: Float<br>    ): IntOffset // this has getters for getting the x and y position.<br><br>    /** This function is used to calculate the height of the layout.<br>     *  [heightOfBubble] and [numberOfBubbles] is used to calculate the y position<br>     *  [topPadding] is the padding from top of the screen<br>     */<br>    fun getLayoutHeight(<br>        numberOfBubbles: Int,<br>        heightOfBubble: Int,<br>        topPadding: Int,<br>        density: Float<br>    ): Int<br>}</pre><ul><li>You can see the implementation here for the 1–2-1 bubble pattern: <a href="https://shorturl.at/akS46">https://shorturl.at/akS46</a></li><li>Detailed Explanation for the same can be found at <a href="https://shorturl.at/rAHX1">https://shorturl.at/rAHX1</a></li></ul><p>Now that we have that capability to get the x and y position for each bubble and also the total height of the bubbles, we just have to use them in our custom layout.</p><pre>@Composable<br>fun BubbleLayout(bubblePlacer: BubblePlacer, bubbles: List&lt;Bubble&gt;, topPadding: Int, modifier: Modifier = Modifier) {<br>    Layout(modifier = modifier, content = {<br>        repeat(bubbles.size) {<br><br>            Bubble(<br>                imageUrl = bubbles[it].imageUrl,<br>                subtitle = bubbles[it].title,<br>            )<br><br>        }<br>    }) { measurables, constraints -&gt;<br>        val bubbles = measurables.map {<br>            it.measure(constraints)<br>        }<br>        val topPaddingPx = (topPadding * density).roundToInt()<br>        val bubbleHeight =<br>            bubbles[0].height // use the first bubble&#39;s height for reference to calculate layout height<br>        val layoutHeight = bubblePlacer.getLayoutHeight(<br>            bubbles.size,<br>            bubbleHeight,<br>            topPaddingPx,<br>            density,<br>        ) // calculate the layout height<br>        layout(constraints.maxWidth, layoutHeight) { <br>            bubbles.forEachIndexed { index, bubble -&gt;<br>                val position = bubblePlacer.place(<br>                    index,<br>                    constraints.maxWidth,<br>                    bubbleHeight,<br>                    bubbles[index].width,<br>                    topPadding = topPaddingPx,<br>                    density<br>                ) // find the position<br>                bubble.place(position.x, position.y) // place it<br><br>            }<br>        }<br>    }</pre><p>and we are done.</p><p>Creating an interface for placing the bubbles and calculating the layout’s height offers us several advantages:</p><ol><li>Separation of UI and Logic: The interface encourages a clear separation between the user interface and the underlying logic. This separation enhances maintainability and allows for the development of reusable layout-related components.</li><li>Scalability: The interface makes it possible to add new bubble patterns in the future without affecting the UI Part of code. For example we have if we want to implement a 2–1–2 pattern, we will just need to a create a new bubble placer and pass it to our custom layout.</li></ol><figure><img alt="" src="https://cdn-images-1.medium.com/max/1022/1*d0wqvQGgkNatXBb8O_rt3w.png" /><figcaption>Example showing 2 different type of layout patterns.</figcaption></figure><p><strong>Final Product </strong>🚀</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/336/1*jGB80ifL4En1rgctBnbFqA.gif" /></figure><p>Credits — <a href="https://medium.com/u/62d308161754">Arun Sharma</a> <a href="https://medium.com/u/1bf9bdb89775">Raj Gohil</a> Thank you for helping and guiding while working on this project.</p><h4>Resources</h4><p><a href="https://developer.android.com/jetpack/compose/layouts/custom">https://developer.android.com/jetpack/compose/layouts/custom</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=8ecf7d010ca8" width="1" height="1" alt=""><hr><p><a href="https://bytes.swiggy.com/building-the-whattoeat-experience-on-android-8ecf7d010ca8">Building the WhatToEat Experience on Android</a> was originally published in <a href="https://bytes.swiggy.com">Swiggy Bytes — Tech Blog</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
    </channel>
</rss>