<?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 Ca-Phun Ung on Medium]]></title>
        <description><![CDATA[Stories by Ca-Phun Ung on Medium]]></description>
        <link>https://medium.com/@caphun?source=rss-5195f2ef511c------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*i1-fQOr20eHdtWdgepeswA.jpeg</url>
            <title>Stories by Ca-Phun Ung on Medium</title>
            <link>https://medium.com/@caphun?source=rss-5195f2ef511c------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Thu, 11 Jun 2026 01:30:40 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@caphun/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[ReactNative: Why Your WebView Disappears inside a ScrollView]]></title>
            <link>https://medium.com/@caphun/reactnative-why-your-webview-disappears-inside-scrollview-c6057c9ac6dd?source=rss-5195f2ef511c------2</link>
            <guid isPermaLink="false">https://medium.com/p/c6057c9ac6dd</guid>
            <category><![CDATA[scrollview]]></category>
            <category><![CDATA[how-to]]></category>
            <category><![CDATA[react-native]]></category>
            <category><![CDATA[javascript]]></category>
            <category><![CDATA[webview]]></category>
            <dc:creator><![CDATA[Ca-Phun Ung]]></dc:creator>
            <pubDate>Sun, 23 Aug 2020 10:22:26 GMT</pubDate>
            <atom:updated>2020-08-23T15:32:41.671Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*MQwNulCDzQDbbY9qDYAzBw.jpeg" /><figcaption>Photo by Brodie Vissers from Burst</figcaption></figure><h3>React Native: Why Your WebView Disappears inside a ScrollView</h3><p>Placing a WebView inside a Scrollview can be problematic. The problem you would immediately come across is the WebView mysteriously disappearing. It IS in fact rendered but merely with a 0px height as it has not inherited the containing ScrollView height.</p><h4>Why would I want to do this?</h4><p>I’m glad you asked. You might be wondering because WebViews naturally support scrolling with scrollEnabled=true . So, it does sound odd why you would wrap it in a ScrollView for this reason alone. You are correct, if that’s all you need then you should stop here. There is no need to complicate things.</p><p>There are however many legitimate reasons. I think by far the most popular is when you want a hero banner or a nice native carousel above the WebView content and would like the native elements and WebView to scroll smoothly. If this sounds like you then read on!</p><h4>How not to do it!</h4><p>To resolve the issue you might have tried this:</p><pre>&lt;ScrollView contentContainerStyle={{flexGrow:1}}&gt;<br>  &lt;WebView /&gt;<br>&lt;/ScrollView&gt;</pre><p>Yes, there is a contentContainerStyle which is applied to the actual View inside the ScrollView that encapsulates child components. This is one step closer to what we’re looking for since now you can actually see the WebView as it is inheriting the ScrollView height correctly.</p><p>However, this is NOT what I consider a working solution as the results are undesirable. If the webpage inside the WebView is taller than the WebView itself the rest of the webpage content is cutoff. Another undesirable result is the inner scroll. The WebView and ScrollView are now both scrollable and interfering with each other. Some developers may try to solve this by disabling WebView scrolling and setting a height to the WebView that is really high in value in order to accommodate the contained webpage content. Again, this is not what I call a solution. That’s just plain laziness!</p><h4>So what’s the solution smart arse?</h4><p>Well, you know how not to do it, and you already know the pitfalls and some half working solutions. Since we already know that the WebView inherits its height from its container and we know that if we set the WebView height tall enough and disable scrolling we will be able to view the entire page up to the set height.</p><p>Setting the height is the key, however, it needs to be set dynamically based on the height of the dynamically loaded webpage content. To do this, we first need to determine the height of the webpage and then somehow relay this message back to the Native side so it could update the WebView height.</p><p>We could put a snippet of JavaScript at the bottom of the webpage page with a postMessage trigger and tell it the page’s body height using DOM properties scrollHeight and offsetHeight:</p><pre>window.ReactNativeWebView.postMessage(<br>  Math.max(document.body.offsetHeight, document.body.scrollHeight)<br>);</pre><p>It’s not always viable to put a snippet in a webpage specific to RNWebView as the webpage might also be served in a browser or published by a third-party. Luckily you don’t have to. You could use WebView’s injectedJavascript to inject this snippet dynamically. Here’s how the solution looks like now:</p><pre>&lt;ScrollView contentContainerStyle={{<br>  flexGrow: 1,<br>  height: webViewHeight <br>}}&gt;<br>  &lt;WebView<br>    scrollEnabled={false}<br>    onMessage={onMessage}<br>    injectedJavaScript={injectedJavaScript}<br>  /&gt;<br>&lt;/ScrollView&gt;</pre><p>Where:</p><pre>const [webViewHeight, setWebViewHeight] = React.useState(null);<br>const onMessage = (event) =&gt; {<br>  setWebViewHeight(Number(event.nativeEvent.data));<br>}<br>const injectedJavaScript=`<br>  window.ReactNativeWebView.postMessage(<br>    Math.max(document.body.offsetHeight, document.body.scrollHeight)<br>  );<br>`</pre><p>That’s it! As simple as that!</p><h4>Thank you!</h4><p>Thanks for reading to the end. Here’s a bonus for those who bothered to get this far! Here’s a fully working Expo Snack example that you can run on a real device and for you to clone, play and break!</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fsnack.expo.io%2Fembedded%2F%40caphun%2Ffbd85a&amp;display_name=Expo+Snack&amp;url=https%3A%2F%2Fsnack.expo.io%2F%40caphun%2Ffbd85a&amp;image=https%3A%2F%2Fs3.amazonaws.com%2Fexp-brand-assets%2FSnackIcon_200.png&amp;key=d04bfffea46d4aeda930ec88cc64b87c&amp;type=text%2Fhtml&amp;schema=expo" width="700" height="500" frameborder="0" scrolling="no"><a href="https://medium.com/media/08d5fdf6749643c97561a563977857e5/href">https://medium.com/media/08d5fdf6749643c97561a563977857e5/href</a></iframe><h4>Further Notes</h4><ul><li>The injected JavaScript snippet is only valid for the first rendered webpage. If your WebView supports browsing then you will have to re-inject the snippet again on each page load.</li></ul><h4>References:</h4><ul><li><a href="https://github.com/react-native-community/react-native-webview/issues/413">https://github.com/react-native-community/react-native-webview/issues/413</a></li><li><a href="https://github.com/facebook/react-native/issues/20347">https://github.com/facebook/react-native/issues/20347</a></li><li><a href="https://github.com/react-native-community/react-native-webview/issues/22">https://github.com/react-native-community/react-native-webview/issues/22</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=c6057c9ac6dd" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[cURL Command Line File Upload]]></title>
            <link>https://medium.com/@caphun/curl-command-line-file-upload-b45cc487c759?source=rss-5195f2ef511c------2</link>
            <guid isPermaLink="false">https://medium.com/p/b45cc487c759</guid>
            <dc:creator><![CDATA[Ca-Phun Ung]]></dc:creator>
            <pubDate>Wed, 30 Jan 2019 20:34:09 GMT</pubDate>
            <atom:updated>2019-01-30T20:35:05.081Z</atom:updated>
            <content:encoded><![CDATA[<p>A quick note (reminder to self) on how to use cURL command line for file upload to an API supporting JSON POST with post fields:</p><pre>curl -X POST \<br>  https://my.api.com/users.json \<br>  -H &#39;Accept: application/json&#39; \<br>  -H &#39;Content-Type: multipart/mixed&#39; \<br>  -F &#39;user[avatar]=@/absolute/path/to/avatar.jpg&#39; \<br>  -F &#39;user[full_name]=Ca-Phun Ung&#39;</pre><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=b45cc487c759" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[React Native: How to Load Local Static Site inside WebView]]></title>
            <link>https://medium.com/@caphun/react-native-load-local-static-site-inside-webview-2b93eb1c4225?source=rss-5195f2ef511c------2</link>
            <guid isPermaLink="false">https://medium.com/p/2b93eb1c4225</guid>
            <category><![CDATA[react-native]]></category>
            <dc:creator><![CDATA[Ca-Phun Ung]]></dc:creator>
            <pubDate>Mon, 14 Jan 2019 19:36:44 GMT</pubDate>
            <atom:updated>2019-04-26T10:42:29.105Z</atom:updated>
            <content:encoded><![CDATA[<h3>React Native: How to Load SPA or Static HTML Site inside WebView</h3><p>React Native WebView allows you to load a publicly accessible resource with the uri property. It works just as a usual website inside an in-app browser. However, it’s not so cut-and-dry when you want to serve it locally as a SPA or static HTML site.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*CugpD67GJkeJ887gof3jlw.jpeg" /></figure><h3>Defining the Problem</h3><p>There are many solutions across the Web on how to serve a site locally from the users device, but most are partial solutions with limited functionality, or fall short of what’s really required to serve a full fledged static site such as ones generated from <a href="https://github.com/facebook/create-react-app">CRA</a>.</p><p>The criteria of a working solution is:</p><ul><li>a site served as ./path/web.html should be no different to one served as http://mysite.com/path/web.html .</li><li>url parameters should work if passed to the local site, as it should to any typical site.</li><li>all static resources such as HTML, JS, CSS, images, and fonts must be stored in the app–without external dependencies–to support offline mode.</li><li>must work the same for both Android and iOS.</li><li>assets must be served from a single location for all platforms to avoid unnecessary duplication.</li></ul><p>As I explored the various solutions to embed a fully working Local Site within a WebView, I realised there is no solution out there that satisfied the above criteria, so I set out to devise one.</p><h3>Devising the Solution</h3><p>Many hours of googling and experimentation lead me to a few critical finds, which form the basis of the entire solution:</p><ol><li>Any folder named with a .bundle extension is seen has a package in Xcode. The contents of the package could be changed any way you want, without affecting the complicated file references Xcode uses to track assets.</li><li>In Xcode you can add folder groups that are linked by reference. i.e. Assets do not have to reside within the ios/MyReactNativeApp folder.</li><li>Android Studio allows you to add multiple Asset folders, and the folder could even be relative to the android/app folder.</li></ol><p>These nuances have worked wonders for me and my team. We’ve been using this approach since late 2017–I think it’s about time we shared our findings to the world–as this method has been tried and tested enough in many of our production apps.</p><p>If you prefer to read code and figure it out yourself, I made a working example on github:</p><p><a href="https://github.com/caphun/react-native-webview-example">caphun/react-native-webview-example</a></p><p>If you don’t mind all the waffling, then please read on, as I finally get down to the nitty gritty of the solution, and take you through a step by step…</p><h3>Step 1 — Setup Web Assets Folder</h3><p>Create a html folder in your RN project root and add a folder named Web.bundle . It should look like this:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/826/1*eq-UHkRFX8_W9V_9CGe9iw.png" /><figcaption>path/to/project/html/Web.bundle</figcaption></figure><p>Next, you need to setup the Android and iOS projects so that they recognise the Web.bundle folder.</p><p>To setup iOS — open the ios folder and double-click the xcodeproj (or xcworkspace if you have one) project file to launch Xcode. Click on the Project Navigator and expand the first folder named after your project name. Have the Finder side-by-side, then drag-in the Web.bundle folder. It should look like this at the end:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/889/1*6hMg0UHAcel83HOyMVA_Ww.png" /><figcaption>Web.bundle Group in Xcode</figcaption></figure><p>When you drop the folder, a dialog will appear. Make sure “Copy items if needed” is <strong>unchecked</strong>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*YY6JruXvF-IUORpi0y1dCw.png" /></figure><p>To setup Android — open path/to/project/android/app/build.gradle file in your editor and add this line inside the android clause like so:</p><pre>android {<br>  ...<br>  sourceSets { <br>    main { assets.srcDirs = [&#39;src/main/assets&#39;, &#39;../../html&#39;] } <br>  }<br>}</pre><p>Alternatively, you could <a href="http://code2care.org/2015/create-assets-folder-in-android-studio/">create the assets folder in Android Studio</a> – When “Change Folder Location” checkbox appears, click it and input ../../html .</p><h3>Step 2 — Add Launcher and Site Files</h3><p>When pointing the Source URI of a WebView to local file resource, there is a major gotcha moment! If you point to {uri: ‘...index.html’} the resource loads without issue but once you pass a parameter, <br>e.g. {uri: ‘...index.html?platform=ios’} , RN WebView will show error like this:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/820/1*1gPQSHISY7sBCsXg6qd58w.png" /></figure><p>To overcome this issue, add a loader.html file toWeb.bundle/loader.html with a single link tag:</p><pre>&lt;a id=&quot;progress-bar&quot; href=&quot;#&quot;&gt;&lt;/a&gt;</pre><p>This will be used in the next Step, more about that later!</p><p>Next, add the actual static local site files to Web.bundle/site/ or any folders and files you wish. You should end up with a structure similar to this:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/776/1*vG6ZYG5s3mTzaQDqKBUlFg.png" /></figure><h3>Step 3 — Putting it all together</h3><p>Now, you should be all set with project files configured and a Local Static Site in place. In this final step I will show you how to glue these things together.</p><p>In Step 1 we setup the Web.bundle so that in iOS you could reference the launcher asWeb.bundle/loader.html and Android as file:///android_asset/Web.bundle/loader.html , see below:</p><pre>const sourceUri = (<br>  Platform.OS === &#39;android&#39; <br>    ? &#39;file:///android_asset/&#39; <br>    : &#39;&#39;<br>) + &#39;Web.bundle/loader.html&#39;;</pre><p>Now, here’s the BIG reveal! Remember in Step 2 you added the launcher file? Well, that’s so we could inject some javascript on initial launch. Yes, it’s a hack! And sorry if this feels like trickery, but a necessary evil in order to pass in launch parameters (which are so very important :), see below:</p><pre>const params = &#39;platform=&#39;+Platform.OS;</pre><pre>const injectedJS = `<br>  if (!window.location.search) {<br>    var link = document.getElementById(&#39;progress-bar&#39;);<br>    link.href = &#39;./site/index.html?${params}&#39;;<br>    link.click();<br>  }<br>`;</pre><p>You may notice in the above snippet I have a !window.location.search condition. That’s to avoid the issue below which occurs only in iOS (as of writing <a href="https://github.com/react-native-community/react-native-webview">react-native-webview</a> is version 3.1.2):</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/820/1*8ubdSR7RjQARNqnrFom4DA.png" /></figure><p>And finally, for best results the WebView should be used with properties specified like so:</p><pre>return &lt;WebView<br>  injectedJavaScript={injectedJS}<br>  source={{ uri: sourceUri }}<br>  javaScriptEnabled={true}<br>  originWhitelist={[&#39;*&#39;]}<br>  allowFileAccess={true}<br>/&gt;;</pre><p>Still here? There’s nothing more to say other than, as mentioned earlier I have a working example on github, clone it, play with it, break it and give feedback!</p><p><a href="https://github.com/caphun/react-native-webview-example">caphun/react-native-webview-example</a></p><h3>Further Notes</h3><ul><li>As of RN0.56.0 the injectedJS hack is not required for Android. Source URI’s to local resources could be directly linked with URL parameters.</li><li>You can avoid the injectedJS hack by using postMessage interface instead. However, you need to setup WebView bridging to facilitate communication between Native and HTML.</li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=2b93eb1c4225" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Parse query strings with one line]]></title>
            <link>https://medium.com/@caphun/a-one-line-query-string-param-parser-e4c672a525b6?source=rss-5195f2ef511c------2</link>
            <guid isPermaLink="false">https://medium.com/p/e4c672a525b6</guid>
            <category><![CDATA[javascript]]></category>
            <dc:creator><![CDATA[Ca-Phun Ung]]></dc:creator>
            <pubDate>Sat, 31 Mar 2018 18:42:30 GMT</pubDate>
            <atom:updated>2018-03-31T19:01:12.269Z</atom:updated>
            <content:encoded><![CDATA[<p>There are many excellent javascript libraries and utilities out there I recommend using if you want to handle query strings in a more systematic way, such as <a href="https://github.com/sindresorhus/query-string">query-string</a>, <a href="http://api.jquery.com/jquery.param/">jquery-param</a>, <a href="https://github.com/unshiftio/url-parse">url-parse</a>, or <a href="https://github.com/medialize/URI.js">URI.js</a>; All convert location.search to a parsed object structure and handle the complex scenarios of arrays, encoding and irregular patterns.</p><p>However, if you sometimes find yourself having to quickly parse a simple query string whilst trying not to introduce external libraries that would pile on the kilobytes, here’s a stab at reduction:</p><pre>(window.location.search.match(/([^=?&amp;]+=[^&amp;]+)/g)||[]).reduce(function(a,b) {<br>  a[b.split(&#39;=&#39;)[0]] = b.split(&#39;=&#39;)[1];<br>  return a; <br>}, {});</pre><p>With arrow functions and Object.assign we end up with one line!</p><pre>(window.location.search.match(/([^=?&amp;]+=[^&amp;]+)/g)||[]).reduce((a,b) =&gt; Object.assign(a, {[b.split(&#39;=&#39;)[0]]: b.split(&#39;=&#39;)[1]}), {})</pre><p>Can you make this any shorter?</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=e4c672a525b6" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>