<?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:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[Full Stack Developer & Amateur Musician]]></title><description><![CDATA[Dairy of a professional software developer and an amateur musician]]></description><link>https://cuneyt.aliustaoglu.biz/en/</link><image><url>http://cuneyt.aliustaoglu.biz/en/favicon.png</url><title>Full Stack Developer &amp; Amateur Musician</title><link>https://cuneyt.aliustaoglu.biz/en/</link></image><generator>Ghost 2.1</generator><lastBuildDate>Sat, 12 Sep 2020 06:07:43 GMT</lastBuildDate><atom:link href="https://cuneyt.aliustaoglu.biz/en/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[Creating a standalone macOS app or dmg file from a JavaFX project]]></title><description><![CDATA[In this tutorial we will create app or dmg file that is ready to be run on macOS systems which does not need to have JRE or JDK installed. We will use IntelliJ and maven for it still can give an idea how to do it using gradle or Eclipse or other variations.]]></description><link>https://cuneyt.aliustaoglu.biz/en/creating-standalone-macos-app-dmg-from-javafx/</link><guid isPermaLink="false">5ed363a2250093415b9206bb</guid><category><![CDATA[JavaFX]]></category><dc:creator><![CDATA[Cuneyt Aliustaoglu]]></dc:creator><pubDate>Sun, 31 May 2020 09:40:42 GMT</pubDate><media:content url="https://cuneyt.aliustaoglu.biz/en/content/images/2020/05/Banner.png" medium="image"/><content:encoded><![CDATA[<img src="https://cuneyt.aliustaoglu.biz/en/content/images/2020/05/Banner.png" alt="Creating a standalone macOS app or dmg file from a JavaFX project"><p>In this tutorial we will create app or dmg file that is ready to be run on macOS systems which does not need to have JRE or JDK installed. We will use IntelliJ and maven, but it still can give an idea how to do it using gradle or Eclipse or other variations.</p><h2 id="prerequisites-">Prerequisites:</h2><ul><li>IntelliJ</li><li>Java 14 (JAVA_HOME)</li><li>JavaFX 14 (PATH_TO_FX)</li></ul><p>If you don't have above setup, please setup your system and environment variables accordingly then tune in here back again.</p><p>openjfx.io has already a very good "Getting Started" page:<a href="https://openjfx.io/openjfx-docs/">https://openjfx.io/openjfx-docs/</a></p><p>We will do the similar but instead of Java12 I will use Java14. You can still use v12 and it should work.</p><figure class="kg-card kg-image-card"><img src="https://cuneyt.aliustaoglu.biz/en/content/images/2020/05/JavaFX-new-project.png" class="kg-image" alt="Creating a standalone macOS app or dmg file from a JavaFX project"><figcaption>Create a new Maven project</figcaption></figure><p>Create a new project and select Maven on the left menu (you may be tempted to click JavaFX but we want to use maven as the orchestrator). In order to add JavaFX library to this project we will use archetype. Use below values for the archetype:</p><table>
<thead>
<tr>
<th>Key</th>
<th>Value</th>
</tr>
</thead>
<tbody>
<tr>
<td>GroupId</td>
<td>org.openjfx</td>
</tr>
<tr>
<td>ArtifactId</td>
<td>javafx-maven-archetypes</td>
</tr>
<tr>
<td>Version</td>
<td>0.0.1</td>
</tr>
</tbody>
</table>
<p>After you add this, archetype will be selected in the list. Without changing this selection click next and GroupId and ArtifactId to you project. Values are up to you but mine was as below:</p><figure class="kg-card kg-image-card"><img src="https://cuneyt.aliustaoglu.biz/en/content/images/2020/05/JavaFX-ArtifactId.png" class="kg-image" alt="Creating a standalone macOS app or dmg file from a JavaFX project"><figcaption>GroupId and ArtifactId</figcaption></figure><p>In the next window change the archetypeArtifactId to "javafx-archetype-fxml". We need this to use xml files for our windows/views. This is similar to layout xml files in Android. Click next and finish creating a new project from archetype. </p><p>This tutorial is only about how to turn this program into a standalone app. So I will not give any details about the code. It's just the bare template. However we will change the version for the <code>javafx-maven-plugin</code> in pom.xml file:</p><pre><code class="language-xml">            &lt;plugin&gt;
                &lt;groupId&gt;org.openjfx&lt;/groupId&gt;
                &lt;artifactId&gt;javafx-maven-plugin&lt;/artifactId&gt;
                &lt;version&gt;0.0.4&lt;/version&gt;
                &lt;configuration&gt;
                    &lt;mainClass&gt;biz.aliustaoglu.App&lt;/mainClass&gt;
                &lt;/configuration&gt;
            &lt;/plugin&gt;
</code></pre>
<p>In <code>Maven Projects</code> pane (View &gt; Tool Windows &gt; Maven Projects) see options <code>javafx:jlink</code> and <code>javafx:run</code></p><figure class="kg-card kg-image-card"><img src="https://cuneyt.aliustaoglu.biz/en/content/images/2020/05/JavaFX-maven-projects.png" class="kg-image" alt="Creating a standalone macOS app or dmg file from a JavaFX project"><figcaption>Maven projects pane</figcaption></figure><p>Now in the console type below commands:</p><pre><code class="language-bash">mvn clean install
mvn javafx:run
</code></pre>
<p>Your basic GUI app from template will run.</p><figure class="kg-card kg-image-card"><img src="https://cuneyt.aliustaoglu.biz/en/content/images/2020/05/JavaFX-run.png" class="kg-image" alt="Creating a standalone macOS app or dmg file from a JavaFX project"><figcaption>JavaFX GUI app is running from source</figcaption></figure><p>Now we want to create a standalone app file. In order to do that first let's create the necessary files:</p><pre><code class="language-bash">mvn javafx:jlink
</code></pre>
<p>This will create a sibling folder for <code>src</code> called <code>target</code>. And inside target/image we have all the files we need in order to run this app in any mac machine. Target macOS machine does not need to have JRE or JDK installed. This is one of Apple's requirements for you to publish apps to App Store. Apps should not require installation of a separate app or framework.</p><p>Go to <code>target/image</code> folder in your terminal and type below:</p><pre><code class="language-bash">./bin/java -m YOUR_GROUPID/YOUR_MAIN_CLASS
# for me it was like below:
# ./bin/java -m biz.aliustaoglu/biz.aliustaoglu.App
</code></pre>
<p>And see that the app is running. Now how to turn this into a single file with .app extension? We will use a small tool:</p><h2 id="appify">Appify</h2><p>Appify is a small bash script to turn this bundle into a single file. Copy this content and paste into a file called <code>appify</code>.</p><script src="https://gist.github.com/mathiasbynens/674099.js"></script><p>You will need to make this file executable using <code>chmod +x appify</code>. Preferably you also need to add its location to PATH environment variable.</p><p>Now create a new file called <code>run.command</code> (filename is not important but we will need to remember this file name for later) and also apply <code>chmod +x run.command</code> command on it and place it under <code>target/image</code> folder.</p><pre><code class="language-bash">#!/bin/bash
cd -- &quot;$(dirname -- &quot;$BASH_SOURCE&quot;)&quot;
cd bin
./java -m biz.aliustaoglu/biz.aliustaoglu.App
exit 0
 
</code></pre>
<p>Now run this command to create the app bundle:</p><pre><code class="language-bash">appify run.command MyJavaFXApp
</code></pre>
<p>After this step you cannot change your app name so choose the name you like at this point. Or you can always repeat this step of course. If you double click this app it will not run. Yet.</p><p>Now copy all the files and folders next to this <code>MyJavaFXApp.app</code> file. Then right click on MyJavaFXApp.app and click <code>Show Package Contents</code> and paste all files into <code>Contents/MacOS/</code> folder. Now you can double click app file and see that it still runs as expected.</p><figure class="kg-card kg-image-card"><img src="https://cuneyt.aliustaoglu.biz/en/content/images/2020/05/javafx-app-bundle.gif" class="kg-image" alt="Creating a standalone macOS app or dmg file from a JavaFX project"><figcaption>Bundle everything</figcaption></figure><h2 id="how-to-create-dmg-file-from-an-app-file">How to Create dmg file from an app file</h2><p>This is the easy part:</p><ul><li>Create a new folder and place MyJavaFXApp.app into it</li><li>Open Disk Utility and click <code>File &gt; New Image &gt; Image From Folder</code></li><li>Select this folder you create in step-1</li><li>Type the dmg file name and save it to disk.</li><li>Now you have a dmg file with app file in it.</li><li>Distribute it to you friends</li></ul><p>Apple will have more requirements for you if you want to publish this app to App Store of course but this step was definitely the most important one.</p>]]></content:encoded></item><item><title><![CDATA[Advanced React Native - ViewGroupManager in Android]]></title><description><![CDATA[React Native is pretty strong with Native Modules. You aren't really limited to using JavaScript. However, documentation for native modules is not that great. You need to dig some functionalities yourself. ViewGroupManager is one of them.]]></description><link>https://cuneyt.aliustaoglu.biz/en/advanced-react-native-viewgroupmanager-android/</link><guid isPermaLink="false">5e2409fa250093415b9206ae</guid><category><![CDATA[React Native]]></category><category><![CDATA[Android]]></category><dc:creator><![CDATA[Cuneyt Aliustaoglu]]></dc:creator><pubDate>Sun, 19 Jan 2020 09:52:29 GMT</pubDate><media:content url="https://cuneyt.aliustaoglu.biz/en/content/images/2020/01/AdvancedReactNative-1.png" medium="image"/><content:encoded><![CDATA[<img src="https://cuneyt.aliustaoglu.biz/en/content/images/2020/01/AdvancedReactNative-1.png" alt="Advanced React Native - ViewGroupManager in Android"><p>React Native is pretty strong with Native Modules. You aren't really limited to using JavaScript. However, documentation for native modules is not that great. You need to dig some functionalities yourself. ViewGroupManager is one of them.</p><p>I will assume that you are familiar with Native Modules and have developed at least one native module using <code>SimpleViewManager&lt;View&gt;</code></p><p><a href="https://facebook.github.io/react-native/docs/native-components-android">https://facebook.github.io/react-native/docs/native-components-android</a></p><p>View's are great placeholders but they cannot contain other views. Let's say you have this simplest Native View.</p><pre><code class="language-java">public class NativeView extends SimpleViewManager&lt;View&gt; {

    public static final String REACT_CLASS = &quot;NativeView&quot;;

    @Override
    public String getName() {
        return REACT_CLASS;
    }

    @Override
    public View createViewInstance(ThemedReactContext context) {
        View v = new View(context);
        v.setBackgroundColor(Color.RED);
        return v;
    }

}
</code></pre>
<p>And call it using JS</p><pre><code class="language-javascript">const NativeView = requireNativeComponent('NativeView')
...

&lt;NativeView style={{ height: 100, width: 100 }} /&gt;
</code></pre>
<p>You will see a square shape (100x100) view with red background. This is what we expect using the code. But if you change the JS code to below:</p><pre><code class="language-js">      &lt;NativeView style={{height: 100, width: 100}}&gt;
        &lt;Text&gt;I am a text view inside a native view&lt;/Text&gt;
      &lt;/NativeView&gt;
</code></pre>
<p>you will get an error:</p><p><code>android.view.View cannot be cast to android.view.ViewGroup</code></p><figure class="kg-card kg-image-card"><img src="https://cuneyt.aliustaoglu.biz/en/content/images/2020/01/image.png" class="kg-image" alt="Advanced React Native - ViewGroupManager in Android"><figcaption>Cast Error</figcaption></figure><p>This is because we are trying to add a textview inside a view. NativeView object is a simple Android View object. It cannot hold another view. Only ViewGroup can hold other views. You need to use layouts if you want to have parent-child relationship with views. Layout classes like LinearLayout or RelativeLayout etc. are sub classes of ViewGroup. </p><p>So instead of <code>SimpleViewManager&lt;View&gt;</code> , we need to use <code>ViewGroupManager&lt;LinearLayout&gt;</code> or <code>ViewGroupManager&lt;ViewGroup&gt;</code> etc. Let's create a new class that extends LinearLayout:</p><pre><code class="language-java">public class NativeLinearLayout extends LinearLayout {
    public NativeLinearLayout(Context context) {
        super(context);
    }
}
</code></pre>
<p>and call it from our main class that used to extend SimpleViewManager:</p><pre><code class="language-java">public class NativeView extends ViewGroupManager&lt;NativeLinearLayout&gt; {

    public static final String REACT_CLASS = &quot;NativeView&quot;;

    @Override
    public String getName() {
        return REACT_CLASS;
    }

    @Override
    public NativeLinearLayout createViewInstance(ThemedReactContext context) {
        NativeLinearLayout nativeLinearLayout = new NativeLinearLayout(context);
        return nativeLinearLayout;
    }

}
</code></pre>
<p>This will work and insert the Text object created from JS into the NativeLinearLayout object.</p><p>We can also create another TextView from inside the native layout and combine them together:</p><figure class="kg-card kg-image-card"><img src="https://cuneyt.aliustaoglu.biz/en/content/images/2020/01/nativelayout.png" class="kg-image" alt="Advanced React Native - ViewGroupManager in Android"><figcaption>Combined text views inside a ViewGroup(LinearLayout)</figcaption></figure><p>But now since you're handling views a bit low level, React Native or CSS is not going to help with styling or the layouts. You need to handle it yourself using the native commands like setLayoutParams etc.</p><p>iOS equivalent of View is UIView and they can hold subviews. So, in iOS you don't need another solution. The default configuration of Native UI Components documentation will just work.</p>]]></content:encoded></item><item><title><![CDATA[Mobile and Desktop Apps with Python, Conda & Kivy]]></title><description><![CDATA[Python is great and it's not only for data scientists. You can also build desktop and mobile apps for Android, iOS, macOS, Linux and Windows. But it's very easy to get stuck because of dependencies and mismatching versions.]]></description><link>https://cuneyt.aliustaoglu.biz/en/mobile-and-desktop-apps-with-python-conda-and-kivy/</link><guid isPermaLink="false">5deb8507250093415b92068c</guid><category><![CDATA[Python]]></category><category><![CDATA[Kivy]]></category><category><![CDATA[Conda]]></category><dc:creator><![CDATA[Cuneyt Aliustaoglu]]></dc:creator><pubDate>Sun, 08 Dec 2019 08:22:38 GMT</pubDate><media:content url="https://cuneyt.aliustaoglu.biz/en/content/images/2019/12/banner-kivy.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://cuneyt.aliustaoglu.biz/en/content/images/2019/12/banner-kivy.jpg" alt="Mobile and Desktop Apps with Python, Conda & Kivy"><p>Python is great and it's not only for data scientists. You can also build desktop and mobile apps for Android, iOS, macOS, Linux and Windows. But it's very easy to get stuck because of dependencies and mismatching versions.</p><p>There are some tools that help us to make apps using Python.</p><h2 id="conda">CONDA</h2><p>Conda is a dependency manager and makes it extremely easy to work with different versions of Python and its dependencies too. To be honest, at first I didn't like using it and did not find it very useful when I  can manage everything using pip commands inside a docker container. Docker and Conda are of course completely 2 different things, but they both try to solve similar problems. </p><p>Docker is more general but still works quite good with Python dependency management especially when you're working with console or web applications. But, Conda is more specialised on managing Python and may be inevitable if you're working with graphical interfaces. So, with kivy library it's better to use Conda.</p><p>Usage is quite straightforward and does not have many commands to remember. So let's install Conda for our environment.</p><p><a href="https://docs.anaconda.com/anaconda/install/">https://docs.anaconda.com/anaconda/install/</a></p><p>After installation, Conda will have a default environment called "base". </p><table>
<thead>
<tr>
<th>Commands</th>
<th>Example</th>
</tr>
</thead>
<tbody>
<tr>
<td>Conda info, version etc.</td>
<td>conda info</td>
</tr>
<tr>
<td>Create a new environment</td>
<td>conda create --name py35 python=3.5</td>
</tr>
<tr>
<td>List environments</td>
<td>conda env list</td>
</tr>
<tr>
<td>Switch to an envirnment</td>
<td>conda activate kivy</td>
</tr>
<tr>
<td>Remove an existing environment</td>
<td>conda env remove --name py35</td>
</tr>
<tr>
<td>Add a channel(respository)</td>
<td>conda config --add channels conda-forge</td>
</tr>
<tr>
<td>Install a library</td>
<td>conda install cython</td>
</tr>
<tr>
<td>Install a library specific version</td>
<td>conda install CUSTOMLIB=0.5.5</td>
</tr>
<tr>
<td>Install a library from a channel</td>
<td>conda install CUSTOMLIB -c conda-forge</td>
</tr>
</tbody>
</table>
<p>And now create a new environment for kivy and Python 3.5 using below command:</p><pre><code>conda create --name kivy python=3.5
conda activate kivy
conda install kivy
</code></pre>
<p>If your terminal supports, you'll see the active environment.  (similar to your active git branch). See below how my default environment (base) changed to kivy.</p><figure class="kg-card kg-image-card"><img src="https://cuneyt.aliustaoglu.biz/en/content/images/2019/12/image.png" class="kg-image" alt="Mobile and Desktop Apps with Python, Conda & Kivy"><figcaption>conda environments</figcaption></figure><h2 id="kivy">Kivy</h2><p>Kivy technically is just another python library. But it's not an ordinary one. It's a framework for creating user interfaces that works on multiple platforms including Android, iOS, Windows, Linux and macOS. You can think it as an alternative to React Native, Flutter, Corona SDK etc. But Kivy is for Python lovers. </p><p>Installing Kivy with Conda is easy.</p><pre><code class="language-bash">conda install kivy -c conda-forge
</code></pre>
<p>Let's create a very simple example from official documents. An accordion controller.</p><p><a href="https://kivy.org/doc/stable/api-kivy.uix.accordion.html">https://kivy.org/doc/stable/api-kivy.uix.accordion.html</a></p><p>Create a file called <code>main.py</code> and fill it with below content:</p><pre><code class="language-python">from kivy.uix.accordion import Accordion, AccordionItem
from kivy.uix.label import Label
from kivy.app import App

class AccordionApp(App):
    def build(self):
        root = Accordion()
        for x in range(5):
            item = AccordionItem(title='Title %d' % x)
            item.add_widget(Label(text='Very big content\n' * 10))
            root.add_widget(item)
        return root


if __name__ == '__main__':
    AccordionApp().run()
</code></pre>
<p>And run:</p><pre><code class="language-bash">python main.py
</code></pre>
<p>That's it. Just a few lines and we have a good looking UI that will work in any environment.</p><figure class="kg-card kg-image-card"><img src="https://cuneyt.aliustaoglu.biz/en/content/images/2019/12/KivyAccordion.png" class="kg-image" alt="Mobile and Desktop Apps with Python, Conda & Kivy"><figcaption>Kivy accordion</figcaption></figure><p>Now let's compile an iOS app from source code. Of course you will need macOS and Xcode for this and other than that we need a tool called kivy-ios.</p><pre><code class="language-bash">git clone https://github.com/kivy/kivy-ios.git 
cd kivy-ios
conda install --yes --file requirements.txt -c conda-forge
pip install pbxproj==2.5.1

# Build kivy-ios. This may take a while
python toolchain.py build python3 kivy
</code></pre>
<p>Note that toolchain.py file is inside this kivy-os folder. So, when you need to run it you may need to use either absolute or relative path unless you're already inside the folder. Also library pbxproj could not be found in any of the conda channels. So that's why I used pip. It's not good to mix pip and conda but if inevitable you can go for it. Last step when we build recipes may take a while.</p><p>Luckily we won't be building kivy-ios for every project. But we will use it to create the Xcode project what we'll be working on.</p><pre><code>python toolchain.py create &lt;title&gt; &lt;app_directory&gt;
</code></pre>
<p>When you execute above command, make sure that app-folder is the one that contains the main.py file. I used it like below:</p><pre><code class="language-bash">python /absolute/path/to/kivy-ios/toolchain.py create my-awesome-app /absolute/path/to/myproject 
</code></pre>
<p>In your project folder next to <code>main.py</code>, A directory named <code>&lt;title&gt;-ios</code> is created. eg. my-awesome-app-ios. Find the xcodeproj and open. Click the run button and see the application is running in iOS simulator. </p><figure class="kg-card kg-image-card"><img src="https://cuneyt.aliustaoglu.biz/en/content/images/2019/12/kivyios.png" class="kg-image" alt="Mobile and Desktop Apps with Python, Conda & Kivy"><figcaption>Kivy app runs inside iOS simulator</figcaption></figure><p>If the run button throws an error, try cleaning the project and running it again. That's what happened to me. Whenever you make a change to python file just click the run button again and it the changes will be reflected. And since this is an Xcode project, app signing and steps needed to submit an app to App Store is no different than a native app.</p><p>Note: I'm not an iOS fan that ignores Android completely but using buildozer for Android is quite troublesome and I couldn't manage to make it run on macOS. I'll do a Part-2 on creating deliverables for Android and other operating systems ;)</p>]]></content:encoded></item><item><title><![CDATA[Phaser3 with Ionic Capacitor, React, Webpack and TypeScript]]></title><description><![CDATA[It may sound like a forced companionship but it actually is a perfect harmony if you want to develop games with Phaser3 for mobile and any desktop environments. Of course you can still use Phaser without Ionic, React or TypeScript but there is too much to gain from the combination. ]]></description><link>https://cuneyt.aliustaoglu.biz/en/phaser3-with-ionic-capacitor-react-webpack-typescript/</link><guid isPermaLink="false">5dbe0d52250093415b920679</guid><category><![CDATA[Game Development]]></category><category><![CDATA[Phaser]]></category><category><![CDATA[React]]></category><category><![CDATA[Ionic]]></category><category><![CDATA[TypeScript]]></category><dc:creator><![CDATA[Cuneyt Aliustaoglu]]></dc:creator><pubDate>Sun, 03 Nov 2019 07:11:11 GMT</pubDate><media:content url="https://cuneyt.aliustaoglu.biz/en/content/images/2019/11/PhaserCapacitor.png" medium="image"/><content:encoded><![CDATA[<img src="https://cuneyt.aliustaoglu.biz/en/content/images/2019/11/PhaserCapacitor.png" alt="Phaser3 with Ionic Capacitor, React, Webpack and TypeScript"><p>It may sound like a forced companionship but it actually is a perfect harmony if you want to develop games with Phaser3 for mobile and any desktop environments. Of course you can still use Phaser without Ionic, React or TypeScript but there is too much to gain from the combination. </p><p>So what do we gain from each?</p><h1 id="components">Components</h1><h3 id="typescript">TypeScript</h3><p>TypeScript provides static type checking. But it also provides code completion in most modern text editors like Vscode, Atom etc. These editors are clever enough to provide code completion even without TypeScript. But this only happens inside the closure or class (depending on your context). Outside the closure, due to JavaScript's dynamic behaviour, editors will not know about the types. So, especially if you're starting to learn a framework (Phaser3 in our case) you do need code completion anywhere in your code. With code completion you will not even need to check documentation every time you type something. Take a look at below example:</p><pre><code class="language-javascript">import road from './road.png'

class MainScene extends Phaser.Scene {
    constructor(){
        this.myRoad = new Road(this)
    }
    
    preload(){
        // You can get code completion here without TypeScript
        this.load.image('road', road)
    }
}

class Road extends Phaser.GameObjects.Container {
    constructor(scene){
        // !!!You won't get any code completion here!!!
        scene.add.image(0, 0, 'road')
    }
}

</code></pre>
<p>Above example, without TypeScript you can still gain from code completion inside the MainScene class. Because, inside the class code editor already knows that "this" refers to "Phaser.Scene" and it will fetch the metadata from this class and will provide you code completion even if you don't use TypeScript. But inside the Road class constructor, editor will not know what the type of the "scene" is, so it won't provide you any code completion. Because it can be anything. Number, string, boolean or a json object. If we used TypeScript, we would be using the Container class as below</p><pre><code class="language-javascript">class Road extends Phaser.GameObjects.Container {
    
    constructor(scene: Phaser.Scene){
        // We get code completion
        scene.add.image(0, 0, 'road')
    }
}


</code></pre>
<figure class="kg-card kg-image-card"><img src="https://cuneyt.aliustaoglu.biz/en/content/images/2019/11/codecompletion-constructor.png" class="kg-image" alt="Phaser3 with Ionic Capacitor, React, Webpack and TypeScript"><figcaption>Yay! Code completion</figcaption></figure><p>I have to admit. For me I love TypeScript because of its code completion rather than static type checking. It's super useful when I'm using learning a new framework. I don't need to go to documentation as it's already provided to me whenever I type a "dot".</p><h3 id="react">React</h3><p>React may not be indispensable for a Phaser3 project. But it may make things easier if you want to use your existing React skills and you don't want to prepare complex GUI for your game. With React you can create place holders for your Phaser.Game to mount. You can create the "About" and "Settings" page inside the React app. Otherwise these pages will need to be a Phaser.Scene which will take a good amount of development time. Of course a game app looks more professional if the all scenes are created within the framework. But even if you later on choose this path, React will give no harm giving a host to your Phaser.Game object other than few more kilobytes to add on top of your bundle. But for me, having a Phaser3 game embedded inside a React gives nice flexibility to scale the app.</p><h3 id="webpack">Webpack</h3><p>A Phaser game is HTML5 but you cannot simply double click index.html file and expect it to work inside the browser. It will need resources such as images, sounds, videos and many different file types. Accessing these files from your file system requires OS level permissions and your browser will throw errors. So you need to run your app from a local server. Webpack will bundle your files and then serve them locally, handle dynamic reloading as you change your code while developing, map the exact code you type to the one that runs inside the browser so you can debug, minify the code and many more. You will not need webpack dev server to run your production ready code. It's only to get to production ready code. When you get that code it's still a static web page and you can host it using Amazon S3, Nginx or any other server. To run it with Android and iOS or on Windows, Linux or macOS; we will use the help of Ionic Capacitor.</p><h3 id="ionic-capacitor">Ionic Capacitor</h3><p>Capacitor is a great new tool from Ionic team that can turn any web app into a cross platform app. It's similar to Cordova but Capacitor makes the process extremely easy and any web app could be turned to an apk, ipa, exe, dmg etc. Capacitor does not dictate you a structure as long as you have a valid package.json file and and output folder that has a starting point. It creates the native bridge for each environment. All you need to do is to focus on your development. </p><p>As opposed to React Native, Ionic Capacitor does not provide you sophisticated debugging tools other than "console.log" but you don't need extreme debugging in native environments. You can debug your app using the browser and expect it to work exactly the same in the native environments. But for rare cases you can use "console.log" and check native environment logs (eg. Logcat for Android) for problem solving.</p><h1 id="application">Application</h1><p>So now let's combine all components to create a boilerplate that we can use for our Phaser3 applications from now on.</p><p>Below repository will be the similar to what we create in this tutorial. However, I may be optimising this repo over time. So, the code may not match exactly but it'll be quite similar.</p><p><a href="https://github.com/aliustaoglu/boilerplates/tree/master/phaser3-capacitor-react-typescript-starter">https://github.com/aliustaoglu/boilerplates/tree/master/phaser3-capacitor-react-typescript-starter</a></p><p>Let's init our node app:</p><pre><code class="language-bash">mkdir phaser3-starter
cd phaser3-starter
npm init
</code></pre>
<p>Now let's add below libraries using yarn</p><pre><code class="language-bash">yarn add -D webpack webpack-cli webpack-dev-server
yarn add -D typescript ts-loader source-map-loader
yarn add -D style-loader css-loader file-loader
yarn add -D @types/react-dom @types/react
</code></pre>
<p>Create a new folder /src and add an empty index.tsx file in it.</p><p>Create webpack.config.js in root folder and add below content:</p><pre><code class="language-javascript">module.exports = {
  mode: process.env.NODE_ENV === &quot;production&quot; ? &quot;production&quot; : &quot;development&quot;,
  devtool: &quot;inline-source-map&quot;,
  entry: &quot;./src/index.tsx&quot;,
  output: {
    path: __dirname + &quot;/build&quot;,
    filename: &quot;bundle.js&quot;,
  },
  devServer: {
    inline: true,
    contentBase: &quot;./build&quot;,
    port: 3000,
  },
  resolve: {
    extensions: [&quot;.ts&quot;, &quot;.tsx&quot;, &quot;.js&quot;],
  },
  module: {
    rules: [
      { test: /\.tsx?$/, exclude: /node_modules/, loader: &quot;ts-loader&quot; },
      { test: /\.js$/, use: [&quot;source-map-loader&quot;], enforce: &quot;pre&quot; },
      { test: /\.css$/, use: [&quot;style-loader&quot;, &quot;css-loader&quot;] },
    ],
  },
  plugins: [],
};
</code></pre>
<p>Create a "tsconfig.json" file inside root folder and below content:</p><pre><code class="language-json">{
  &quot;compilerOptions&quot;: {
    &quot;outDir&quot;: &quot;./www/&quot;,
    &quot;sourceMap&quot;: true,
    &quot;noImplicitAny&quot;: true,
    &quot;module&quot;: &quot;commonjs&quot;,
    &quot;target&quot;: &quot;es5&quot;,
    &quot;jsx&quot;: &quot;react&quot;,
    &quot;resolveJsonModule&quot;: true,
    &quot;skipLibCheck&quot; : true,
    &quot;esModuleInterop&quot;: true
  },
  &quot;include&quot; : [
      &quot;./src/**/*&quot;
  ],
  &quot;exclude&quot; : [
      &quot;./test/**/*&quot;
  ]
}
</code></pre>
<p>Change package.json file so it's like below:</p><pre><code class="language-json">{
  &quot;scripts&quot;: {
    &quot;test&quot;: &quot;echo \&quot;Error: no test specified\&quot; &amp;&amp; exit 1&quot;,
    &quot;start&quot;: &quot;webpack-dev-server --open&quot;,
    &quot;devbuild&quot;: &quot;webpack --mode development&quot;,
    &quot;build&quot;: &quot;webpack --mode production&quot;
  }
}

</code></pre>
<p>Now if you run "npm start" your terminal should compile everything successfully and open a blank page in your browser. It will say "Cannot GET /" but that's because we have no content.</p><p>Let's create a new folder called "www" and index.html inside it with below content:</p><pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;en&quot;&gt;
&lt;head&gt;
    &lt;meta charset=&quot;UTF-8&quot;&gt;
    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot;&gt;
    &lt;meta http-equiv=&quot;X-UA-Compatible&quot; content=&quot;ie=edge&quot;&gt;
    &lt;title&gt;&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;div id=&quot;root&quot;&gt;&lt;/div&gt;
    &lt;script type=&quot;text/javascript&quot; src=&quot;bundle.js&quot;&gt;&lt;/script&gt;
&lt;/html&gt;
</code></pre>
<p>Now "npm start" should still have no errors in console and open an empty page with no errors. Now TypeScript and Webpack running smoothly let's add React.</p><pre><code class="language-bash">yarn add react react-dom
</code></pre>
<p>Enter below content to "src/index.tsx"</p><pre><code class="language-javascript">import React from 'react'
import ReactDOM from 'react-dom'
import App from './components/App'

ReactDOM.render(&lt;App /&gt;, document.getElementById('root'))

</code></pre>
<p>Create a new folder called "components" inside and add App.tsx file so it's location "src/components/App.tsx"</p><pre><code class="language-js">import React from 'react'

type AppProps = {}
type AppState = {}

class App extends React.Component&lt;AppProps, AppState&gt; {
  constructor(props: AppProps) {
    super(props)
  }

  render() {
    return &lt;&gt;Hello from React&lt;/&gt;
  }
}

export default App

</code></pre>
<blockquote>npm start</blockquote><p>And see React hello world app is running:</p><figure class="kg-card kg-image-card"><img src="https://cuneyt.aliustaoglu.biz/en/content/images/2019/11/react-hello.png" class="kg-image" alt="Phaser3 with Ionic Capacitor, React, Webpack and TypeScript"><figcaption>Hello from React</figcaption></figure><p>Now let's Phaser libraries:</p><blockquote>yarn add phaser</blockquote><p>Change render function of App.tsx file:</p><pre><code class="language-js">  render() {
    return &lt;&gt;
      &lt;div id=&quot;game-root&quot;&gt;&lt;/div&gt;
      This is React
    &lt;/&gt;
  }
</code></pre>
<p>Add a new folder /src/game and create file MainScene.ts. Note that this is a "ts" file not "tsx".</p><pre><code class="language-js">import Phaser from 'phaser'

class MainScene extends Phaser.Scene {
  constructor() {
    super('MainScene')
  }
}

export default MainScene

</code></pre>
<p>Now change the content of App.tsx file as below:</p><pre><code class="language-js">
import React from 'react'
import Phaser from 'phaser'
import MainScene from '../game/MainScene'

type AppProps = {}
type AppState = {}

class App extends React.Component&lt;AppProps, AppState&gt; {
  constructor(props: AppProps) {
    super(props)
    const game = new Phaser.Game({
      parent: 'game-root',
      type: Phaser.AUTO,
      width: 300,
      height: 500,
      scene: [MainScene]
    })
  }

  render() {
    return &lt;&gt;
      &lt;div id=&quot;game-root&quot;&gt;&lt;/div&gt;
      This is React
    &lt;/&gt;
  }
}

export default App

</code></pre>
<p>We create a new Phaser.Game instance with MainScene. The game is mounted inside the div with id="game-root". </p><figure class="kg-card kg-image-card"><img src="https://cuneyt.aliustaoglu.biz/en/content/images/2019/11/react-phaser-app.png" class="kg-image" alt="Phaser3 with Ionic Capacitor, React, Webpack and TypeScript"><figcaption>Empty Phase game inside React app</figcaption></figure><p>Now let's add some geometric shapes inside this blank app. Just add a new function inside MainScene.ts file</p><pre><code class="language-js">create(){
    this.add.rectangle(100,100, 100, 100, 0xff0000)
  }
</code></pre>
<figure class="kg-card kg-image-card"><img src="https://cuneyt.aliustaoglu.biz/en/content/images/2019/11/phaser-rectangle.png" class="kg-image" alt="Phaser3 with Ionic Capacitor, React, Webpack and TypeScript"><figcaption>Phaser with rectangle</figcaption></figure><p>Now let's add the final step. Add Ionic Capacitor library so we can turn it into Android (or iOS) app. </p><p><a href="https://capacitor.ionicframework.com/docs/getting-started/">https://capacitor.ionicframework.com/docs/getting-started/</a></p><pre><code class="language-bash">yarn add -D @capacitor/cli @capacitor/core
</code></pre>
<p>Below command will create config file for capacitor. All it needs is a /www folder that has the bundle. Run <code>npm run build</code> before if you haven't done already.</p><pre><code class="language-bash">npx cap init
</code></pre>
<p>Notice that it creates a file called "capacitor.config.json" inside root folder.</p><p>Let's create Android project from this web project</p><pre><code class="language-bash">npx cap add android
</code></pre>
<p>And open Android Studio:</p><pre><code class="language-bash">npx cap open android
</code></pre>
<p>Let the gradle build for sometime and run it using emulator or physical device:</p><figure class="kg-card kg-image-card"><img src="https://cuneyt.aliustaoglu.biz/en/content/images/2019/11/phaser-android.png" class="kg-image" alt="Phaser3 with Ionic Capacitor, React, Webpack and TypeScript"><figcaption>Hello Phaser Android</figcaption></figure><p>You can do the same with iOS or Electron.</p><p>Of course we have a very quick and dirty setup and our app does not look like a game. It does not even look like an app. But this is your boilerplate. Change it, add spritesheets and make it look like a game. And let the Capacitor, Webpack, React and TypeScript do the dirty job for you.</p>]]></content:encoded></item><item><title><![CDATA[Using TomTom Maps in React Native: Part 2 - IOS]]></title><description><![CDATA[In the second part, we will implement the same solution for IOS. After implementing this solution for both environments, we're no longer obligated to use Google Maps in React Native]]></description><link>https://cuneyt.aliustaoglu.biz/en/using-tomtom-maps-in-react-native-part-2-ios/</link><guid isPermaLink="false">5d607050250093415b92066c</guid><category><![CDATA[TomTom]]></category><dc:creator><![CDATA[Cuneyt Aliustaoglu]]></dc:creator><pubDate>Sat, 24 Aug 2019 06:23:58 GMT</pubDate><media:content url="https://cuneyt.aliustaoglu.biz/en/content/images/2019/08/Cover-1.png" medium="image"/><content:encoded><![CDATA[<img src="https://cuneyt.aliustaoglu.biz/en/content/images/2019/08/Cover-1.png" alt="Using TomTom Maps in React Native: Part 2 - IOS"><p>In the first part, I have covered how to use native modules in Android for TomTom maps. Please take a look if you haven't already:</p><p><a href="https://cuneyt.aliustaoglu.biz/en/using-tomtom-maps-in-react-native-with-android/">https://cuneyt.aliustaoglu.biz/en/using-tomtom-maps-in-react-native-with-android/</a></p><p>We want to use the same JavaScript file for both environments. </p><pre><code class="language-javascript">import React from 'react'
import { View, requireNativeComponent, StyleSheet, Platform } from 'react-native'

// IOS removes the suffix &quot;Manager&quot; from the native module if there is one
const moduleName = Platform.OS === 'ios' ? 'MapView' : 'MapViewManager'

const TomTomMap = requireNativeComponent(moduleName)

class App extends React.Component {
  constructor(props){
    super(props)
  }
  onMapReady = () =&gt; {
    alert('MAP READY')
  }

  render () {
    return (
      &lt;&gt;
        &lt;TomTomMap
          onMapReady={this.onMapReady}
          markers={[{ lat: 40.9175, lng: 38.3927, label: 'Giresun' }, { lat: 40.9862, lng: 37.8797, label: 'Ordu' }]}
          mapZoom={7}
          mapCenter={{ lat: 40.9175, lng: 38.3927 }}
          style={StyleSheet.absoluteFillObject}
        /&gt;
      &lt;/&gt;
    )
  }
}

export default App

</code></pre>
<p>Now let's start Xcode and add native modules to use TomTom map with React Native.</p><p>Add a "New Group with Folder" and type "NativeModules", so we will separate RN's boilerplate from our code.</p><p>Inside NativeModules create a new Objective C file and call it NativeBridge. Also create a new Swift file called "MapViewManager.swift". It will warn you to add a bridging header. Click "Create Bridging Header" and don't change the default name.</p><p>Add another swift file called TomTomMap. After creating all the files the folder structure should look like below:</p><figure class="kg-card kg-image-card"><img src="https://cuneyt.aliustaoglu.biz/en/content/images/2019/08/Screen-Shot-2019-08-24-at-17.21.09.png" class="kg-image" alt="Using TomTom Maps in React Native: Part 2 - IOS"><figcaption>Our folder structure for IOS native modules</figcaption></figure><p>Those files are all empty. Let's implement the same functionality that we've done in Android.</p><p>One important thing is that if an IOS native component ends with suffix 'Manager', React Native ignores it. For example our component is called MapViewManager but when we 'require' this component from JavaScript we need to require it as MapView not MapViewManager. But this does not apply to Android for some reason. So I had to  add below to JavaScript file.</p><pre><code class="language-javascript">const moduleName = Platform.OS === 'ios' ? 'MapView' : 'MapViewManager'
const TomTomMap = requireNativeComponent(moduleName)
</code></pre>
<p>We will implement this solution in Swift. But to export Swift classes and functions to JavaScript, we need to use Objective-C as the bridge. When Swift and Objective-C files exist together in a project, Xcode creates a file called Bridging-Header. That's why Xcode generated that file automatically after you added a Swift file to the project.</p><p>Before we start we need to add TomTom map library. Since version 60, React Native is using CocoaPods by default. Add below to your Podfile:</p><pre><code>pod 'TomTomOnlineSDKMaps'
</code></pre>
<p>If you want to use all modules you can add the all using below. But for this tutorial we will only use the map not the APIs.</p><pre><code>  pod 'TomTomOnlineSDKMaps'
  pod 'TomTomOnlineSDKSearch'
  pod 'TomTomOnlineSDKRouting'
  pod 'TomTomOnlineSDKTraffic'
  pod 'TomTomOnlineSDKMapsUIExtensions'
  pod 'TomTomOnlineSDKMapsStaticImage'
  pod 'TomTomOnlineSDKGeofencing'
</code></pre>
<p>Now edit info.plist file and add your API key. We only need to add OnlineMap.Key for this tutorial.</p><figure class="kg-card kg-image-card"><img src="https://cuneyt.aliustaoglu.biz/en/content/images/2019/08/info_plist.png" class="kg-image" alt="Using TomTom Maps in React Native: Part 2 - IOS"><figcaption>Info.plist</figcaption></figure><p>Now under ./ios folder run <code>pod install</code> to install the dependencies.</p><p>We've installed the IOS SDK. So now let's add below to NativeBridge.m file</p><pre><code class="language-objectivec">#import &lt;Foundation/Foundation.h&gt;
#import &quot;React/RCTViewManager.h&quot;

@interface RCT_EXTERN_MODULE(MapViewManager, RCTViewManager)
  RCT_EXPORT_VIEW_PROPERTY(mapZoom, NSNumber)
  RCT_EXPORT_VIEW_PROPERTY(mapCenter, NSDictionary)
  RCT_EXPORT_VIEW_PROPERTY(markers, NSArray)
  RCT_EXPORT_VIEW_PROPERTY(onMapReady, RCTDirectEventBlock)
@end

</code></pre>
<p>It may look familiar. Those are the props that we send from JavaScript. ReadableMap in Java becomes NSDictionary and ReadableArray becomes NSArray in ObjectiveC and Swift. We don't implement anything in Objective C. It is just an interface. We will create the implementation in a better looking language called Swift :)</p><p>Edit MapViewManager.swift file as below</p><pre><code class="language-swift">import Foundation
import TomTomOnlineSDKMaps

@objc(MapViewManager)
class MapViewManager: RCTViewManager {
  
  override func view() -&gt; TTMapView {
    let ttMap = TomTomMap(frame: CGRect(x:0, y:0, width: 0, height: 0))
    return ttMap
  }
}
</code></pre>
<p>Here RCTViewManager is equivalent to the SimpleViewManager in Java. It is also just a wrapper and will pass all the logic into the real map component.</p><p>Edit TomTomMap.swift file as below:</p><pre><code class="language-swift">import Foundation
import TomTomOnlineSDKMaps

class TomTomMap: TTMapView {
  
  @objc var mapZoom: NSNumber!
  @objc var mapCenter: NSDictionary = [:]
  @objc var onMapReady: RCTDirectEventBlock?
  @objc var markers: NSArray!
  
  override init(frame: CGRect) {
    super.init(frame: frame)
  }
  
  override func didSetProps(_ changedProps: [String]!) {
    let mapView = TTMapView(frame: self.bounds)
    let lat = self.mapCenter.object(forKey: &quot;lat&quot;) as! Double
    let lng = self.mapCenter.object(forKey: &quot;lng&quot;) as! Double
    
    let zoom = self.mapZoom as! Double
    
    for i in 0...self.markers.count-1 {
      let marker = self.markers[i] as! NSDictionary
      let mLat = marker.object(forKey: &quot;lat&quot;) as! Double
      let mLng = marker.object(forKey: &quot;lng&quot;) as! Double
      let lbl = marker.object(forKey: &quot;label&quot;) as! String
      
      let anno = TTAnnotation(coordinate: CLLocationCoordinate2D( latitude: mLat,longitude: mLng))
      self.annotationManager.add(anno)
    }
    
    mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
    self.center(on: CLLocationCoordinate2D( latitude: lat,longitude: lng), withZoom: zoom )
    
    self.onMapReady!([:])
  }
  
  required init(coder aDecoder: NSCoder) {
    fatalError(&quot;init(coder:) has not been implemented&quot;)
  }
  
  
}

</code></pre>
<p>First 4 lines of the class are mapped to ObjectiveC file which links it to the JavaScript file as props. @objc decorator is needed when we want to use a Swift variable in ObjectiveC.</p><p><strong>@objc var mapZoom: NSNumber!<br>@objc var mapCenter: NSDictionary = [:]<br>@objc var onMapReady: RCTDirectEventBlock?<br>@objc var markers: NSArray!</strong></p><p>This decorator creates the setters automatically. So, whenever a prop gets changed, it triggers "didSetProps" function with changedProps parameter with the changed prop names list. This is the parameter you want to check when you want to optimize this function. </p><p>As we did in Java, we are setting the zoom, map center and 2 markers. And also triggers onMapReady callback in JavaScript. Now run the project in Xcode and start the simulator.</p><figure class="kg-card kg-image-card"><img src="https://cuneyt.aliustaoglu.biz/en/content/images/2019/08/android-ios-results.png" class="kg-image" alt="Using TomTom Maps in React Native: Part 2 - IOS"><figcaption>Results in both Android and IOS - Looks consistent</figcaption></figure><p>So, you can use this simple project as a starting point and extend it further if you don't want to use Google Maps in React Native.</p>]]></content:encoded></item><item><title><![CDATA[Using TomTom Maps in React Native: Part 1 - Android]]></title><description><![CDATA[There's no reliable React Native library for TomTom maps. The only way is to implement your own solution using the native SDK. So, I wanted to write a guide for those who are struggling to use native SDKs for React Native. ]]></description><link>https://cuneyt.aliustaoglu.biz/en/using-tomtom-maps-in-react-native-with-android/</link><guid isPermaLink="false">5d5f8ea3250093415b920667</guid><category><![CDATA[React Native]]></category><category><![CDATA[TomTom]]></category><dc:creator><![CDATA[Cuneyt Aliustaoglu]]></dc:creator><pubDate>Fri, 23 Aug 2019 22:54:42 GMT</pubDate><media:content url="https://cuneyt.aliustaoglu.biz/en/content/images/2019/08/Cover.png" medium="image"/><content:encoded><![CDATA[<img src="https://cuneyt.aliustaoglu.biz/en/content/images/2019/08/Cover.png" alt="Using TomTom Maps in React Native: Part 1 - Android"><p>There's no reliable React Native library for TomTom maps. The only way is to implement your own solution using the native SDK. Luckily, TomTom maps has good documentation and an easy to understand SDK. But still, those documents are prepared for those who are using the native environment. Activity, Fragment, Context etc. do not really mean anything to a React Native developer. So, I wanted to write a guide for those who are struggling to use native SDKs for React Native. </p><p>Let's start from scratch. First init a new application:</p><pre><code class="language-sh">react-native init reactNativeTomTomMap
cd reactNativeTomTomMap
</code></pre>
<p>Now run Android Studio and open reactNativeTomTomMap/android folder. For most of the parts, we just need to follow official Android SDK documentation. But since we're developing a React Native app, not a "native" Native app, some parts will be different. </p><p><a href="https://developer.tomtom.com/maps-android-sdk/map-initialization">https://developer.tomtom.com/maps-android-sdk/map-initialization</a></p><h2 id="installation">Installation</h2><p>On the left menu under Gradle Sripts, open build.gradle (Module:app) file and below line under dependencies:</p><pre><code class="language-gradle">dependencies{
    ....
    implementation(&quot;com.tomtom.online:sdk-maps:2.4207&quot;)
    ....
}
</code></pre>
<p>TomTom SDK does not exist in the default repository, so we need to tell Android Studio where to find the SDK. Now edit below file:</p><p>build.gradle (Project: reactNativeTomTomMap)</p><pre><code class="language-gradle">allprojects{
  ...
  repositories{
    ...
    maven {
         url 'https://maven.tomtom.com:8443/nexus/content/repositories/releases/'
      }
    ...
  }
}
</code></pre>
<p>If you try to sync gradle, you might get this error:</p><code style="color: red;">
    ERROR: Manifest merger failed : Attribute application@allowBackup value=(false) from AndroidManifest.xml:11:7-34
	is also present at [com.tomtom.online:sdk-location:2.4207] AndroidManifest.xml:16:9-35 value=(true).
	Suggestion: add 'tools:replace="android:allowBackup"' to <application> element at AndroidManifest.xml:7:5-117 to override.
</application></code><p>So, let's do the suggested solution and add <code>tools:replace="android:allowBackup"</code> to AndroidManifest.xml. But, make sure to add <code>xmlns:tools="http://schemas.android.com/tools"</code> to manifest element. File will look something like below:</p><pre><code class="language-xml">&lt;manifest xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
    xmlns:tools=&quot;http://schemas.android.com/tools&quot;
  package=&quot;com.reactnativetomtommap&quot;&gt;
    
    &lt;application
      android:name=&quot;.MainApplication&quot;
      android:label=&quot;@string/app_name&quot;
      android:icon=&quot;@mipmap/ic_launcher&quot;
      android:roundIcon=&quot;@mipmap/ic_launcher_round&quot;
      android:allowBackup=&quot;false&quot;
      android:theme=&quot;@style/AppTheme&quot;
      tools:replace=&quot;android:allowBackup&quot;
        &gt;
</code></pre>
<p>Now, if you try to sync you might get below error:</p><p><code>Cannot fit requested classes in a single dex file (# methods: 85892 &gt; 65536)</code></p><p>For that, we need to enable multiDex. </p><p>Look at <a href="https://developer.android.com/studio/build/multidex">https://developer.android.com/studio/build/multidex</a> for more info. Add this to build.gradle(module: app).</p><pre><code class="language-groovy">android {
....
    defaultConfig {
        ....
        multiDexEnabled true
    }
....
}
....
dependencies {
    ....
    implementation 'com.android.support:multidex:1.0.3'
    ....
}
</code></pre>
<p>Now it should sync without any errors. But if you run the application, it may fail. multiDex can only be enabled with minSdkVersion is 21 or above. Make sure to make that change:</p><pre><code class="language-groovy">buildscript {
    ext {
        ....
        minSdkVersion = 21
        ....
    }
}
</code></pre>
<p>We need to add TomTom API key to AndroidManifest. Create a free account on tomtom.com and generate a new key and add it into AnroidManifest.</p><pre><code class="language-xml">&lt;meta-data
            android:name=&quot;OnlineMaps.Key&quot;
            android:value=&quot;PASTE_API_KEY_HERE&quot; /&gt;
</code></pre>
<h2 id="implementing-a-static-native-map">Implementing a Static Native Map</h2><p>Now let's setup a Native UI module:</p><p>Under <code>app &gt; java &gt; com.reactnativetomtommap</code> create a new package called NativeModules. And under NativeModules, create a file called MapViewManager.java</p><p>We will extend it further. But for now let's just implement it as below.</p><pre><code class="language-java">package com.reactnativetomtommap.NativeModules;

import android.view.View;
import android.widget.TextView;

import com.facebook.react.uimanager.SimpleViewManager;
import com.facebook.react.uimanager.ThemedReactContext;

import javax.annotation.Nonnull;

public class MapViewManager extends SimpleViewManager&lt;View&gt; {
    @Nonnull
    @Override
    public String getName() {
        return &quot;MapViewManager&quot;;
    }

    @Nonnull
    @Override
    protected View createViewInstance(@Nonnull ThemedReactContext reactContext) {
        TextView tw = new TextView(reactContext);
        tw.setText(&quot;Map component will be here&quot;);
        return tw;
    }
}

</code></pre>
<p>under com.reactnativetomtommap package, create a new file called NativePackage that implements ReactPackage and registers MapViewManager as a module.</p><pre><code class="language-java">package com.reactnativetomtommap;

import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;
import com.reactnativetomtommap.NativeModules.MapViewManager;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class NativePackage implements ReactPackage {
    @Override
    public List&lt;NativeModule&gt; createNativeModules(ReactApplicationContext reactContext) {
        List&lt;NativeModule&gt; modules = new ArrayList&lt;&gt;();
        return modules;
    }

    @Override
    public List&lt;ViewManager&gt; createViewManagers(ReactApplicationContext reactContext) {
        return Arrays.&lt;ViewManager&gt;asList(
                new MapViewManager()
        );
    }
}

</code></pre>
<p>My left panel in Android Studio looks like this:</p><figure class="kg-card kg-image-card"><img src="https://cuneyt.aliustaoglu.biz/en/content/images/2019/08/AndroidPanel-1.png" class="kg-image" alt="Using TomTom Maps in React Native: Part 1 - Android"><figcaption>Native Modules setup</figcaption></figure><p>In MainApplication.java make below changes:</p><pre><code class="language-java">@Override
    protected List&lt;ReactPackage&gt; getPackages() {
      @SuppressWarnings(&quot;UnnecessaryLocalVariable&quot;)
      List&lt;ReactPackage&gt; packages = new PackageList(this).getPackages();
      // Packages that cannot be autolinked yet can be added manually here, for example:
      // packages.add(new MyReactNativePackage());
      packages.add(new NativePackage()); // ADD THIS LINE
      return packages;
    }
</code></pre>
<p>In App.js, remove all content and replace it with below:</p><pre><code class="language-js">import React from 'react'
import { View, requireNativeComponent, StyleSheet } from 'react-native'

const TomTomMap = requireNativeComponent('MapViewManager')

const App = () =&gt; {
  return &lt;&gt;
  &lt;TomTomMap style={StyleSheet.absoluteFillObject} /&gt;
  &lt;/&gt;
}

export default App

</code></pre>
<p>You should be able to sync and run the application without any errors. You will see a simple text "Map component will be here". Now we are ready to replace that text with a real TomTom map :) Replace createViewInstance method as below and see your success:</p><pre><code class="language-java">@Nonnull
    @Override
    protected View createViewInstance(@Nonnull ThemedReactContext reactContext) {
        MapView mapView = new MapView(reactContext);
        return  mapView;
    }
</code></pre>
<figure class="kg-card kg-image-card"><img src="https://cuneyt.aliustaoglu.biz/en/content/images/2019/08/Hello-TomTom.png" class="kg-image" alt="Using TomTom Maps in React Native: Part 1 - Android"><figcaption>Hello React Native TomTom&nbsp;</figcaption></figure><p>It's very good so far but it's just a static map. Let's try to control this map from JavaScript as it's the whole point we're using React Native.</p><h2 id="implementing-a-dynamic-native-map">Implementing a Dynamic Native Map</h2><p>Under NativeModules package, create a new class called TomTomMap and extend it from MapView of TomTom library. Instead of the ViewManager, we will use this class to interact with the map itself. ViewManager will be the place where we will manage the bridge between JavaScript and Java.</p><pre><code class="language-java">package com.reactnativetomtommap.NativeModules;

import android.content.Context;

import androidx.annotation.NonNull;

import com.tomtom.online.sdk.map.MapView;

public class TomTomMap extends MapView {
    public TomTomMap(@NonNull Context context) {
        super(context);
    }


}

</code></pre>
<p>And edit MapViewManager class as below:</p><pre><code class="language-java">    @Nonnull
    @Override
    protected View createViewInstance(@Nonnull ThemedReactContext reactContext) {
        TomTomMap tomTomMap = new TomTomMap(reactContext);
        tomTomMap.onResume();
        return tomTomMap;
    }
</code></pre>
<p>Map is still static but we just separated the View from ViewManager. Now is the time to add some @ReactProp. If you're not familiar with adding native UI modules, take a look at the documentation below and come back.</p><p><a href="https://facebook.github.io/react-native/docs/native-components-android">https://facebook.github.io/react-native/docs/native-components-android</a></p><p>Now I want to zoom, set the center of the map and add some markers from JavaScript:</p><pre><code class="language-jsx">
onMapReady(){
  alert('Map is ready!')
}

&lt;TomTomMap
  onMapReady={this.onMapReady}
  markers={[{ lat: 40.9175, lng: 38.3927, label: 'Giresun' }, { lat: 40.9862, lng: 37.8797, label: 'Ordu' }]}
  mapZoom={7}
  mapCenter={{ lat: 40.9175, lng: 38.3927 }}
  style={StyleSheet.absoluteFillObject}
/&gt;

</code></pre>
<p>Let's prepare native side for those props:</p><p>Edit MapViewManager as below. ViewManager gets the props from JavaScript and passes it to our TomTomMap class extended from MapView. There are 2 types of props. First one is object props which could be a primitive JS object, any JSON object or an array. Second one is the callback props eg. onSomethingReady, onSomethingLoad, onSomethingClick etc. Examine the code below:</p><pre><code class="language-java">package com.reactnativetomtommap.NativeModules;

import android.view.View;


import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.common.MapBuilder;
import com.facebook.react.uimanager.SimpleViewManager;
import com.facebook.react.uimanager.ThemedReactContext;
import com.facebook.react.uimanager.annotations.ReactProp;


import java.util.Map;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;

public class MapViewManager extends SimpleViewManager&lt;View&gt; {
    @Nonnull
    @Override
    public String getName() {
        return &quot;MapViewManager&quot;;
    }

    @Nonnull
    @Override
    protected View createViewInstance(@Nonnull ThemedReactContext reactContext) {
        TomTomMap tomTomMap = new TomTomMap(reactContext);
        tomTomMap.onResume();
        return tomTomMap;
    }

    @ReactProp(name = &quot;mapCenter&quot;)
    public void setCenter(TomTomMap mapView, @Nullable ReadableMap center) {
        mapView.setMapCenter(center);
    }

    @ReactProp(name = &quot;markers&quot;)
    public void setMarkers(TomTomMap mapView, @Nullable ReadableArray markers) {
        mapView.setMarkers(markers);
    }

    @ReactProp(name = &quot;mapZoom&quot;)
    public void setMapZoom(TomTomMap mapView, @Nullable Integer zoom) {
        mapView.setMapZoom(zoom);
    }


    @Nullable
    @Override
    public Map getExportedCustomBubblingEventTypeConstants() {
        return MapBuilder.builder()
                .put(&quot;onMapReady&quot;, MapBuilder.of(&quot;phasedRegistrationNames&quot;, MapBuilder.of(&quot;bubbled&quot;, &quot;onMapReady&quot;)))
                .build();
    }


}


</code></pre>
<p>First set of props are gathered using @ReactProp. To read JavaScript's JSON object we need to use Java's ReadableMap and for. Same relationship is between Array and ReadableArray. You cannot set the callbacks using the ReactProp decorator. Every event should be registered as a bubbling event type.</p><p>Now edit the TomTomMap class as below and see how it manipulates the the real map object to add markers, zoom and set center.</p><pre><code class="language-java">package com.reactnativetomtommap.NativeModules;

import android.content.Context;

import androidx.annotation.NonNull;

import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.uimanager.events.RCTEventEmitter;
import com.tomtom.online.sdk.common.location.LatLng;
import com.tomtom.online.sdk.map.CameraPosition;
import com.tomtom.online.sdk.map.MapView;
import com.tomtom.online.sdk.map.MarkerBuilder;
import com.tomtom.online.sdk.map.OnMapReadyCallback;
import com.tomtom.online.sdk.map.SimpleMarkerBalloon;
import com.tomtom.online.sdk.map.TomtomMap;

public class TomTomMap extends MapView implements OnMapReadyCallback {

    private int _zoom;
    private ReadableArray _markers;
    private ReadableMap _center;
    private Boolean isMapReady = false;
    private TomtomMap _tomTomMap;


    public TomTomMap(@NonNull Context context) {
        super(context);
        this.addOnMapReadyCallback(this);
    }

    protected void setMapCenter(ReadableMap center) {
        _center = center;
        if (isMapReady){
            _tomTomMap.centerOn(_center.getDouble(&quot;lat&quot;), center.getDouble(&quot;lng&quot;));
        }
    }

    protected void setMarkers(ReadableArray markers) {
        _markers = markers;
        if (isMapReady){
            _tomTomMap.removeMarkers();
            drawMarkers();
        }
    }

    protected void setMapZoom(Integer zoom) {
        _zoom = zoom;
        if (isMapReady){
            _tomTomMap.zoomTo(_zoom);
        }
    }


    @Override
    public void onMapReady(@NonNull TomtomMap tomtomMap) {
        isMapReady = true;
        _tomTomMap = tomtomMap;
        reactNativeEvent(&quot;onMapReady&quot;, null);
        LatLng mapCenter = new LatLng(_center.getDouble(&quot;lat&quot;), _center.getDouble(&quot;lng&quot;));
        tomtomMap.centerOn(CameraPosition.builder(mapCenter).zoom(_zoom).build());
        drawMarkers();
    }

    private void drawMarkers(){
        for (int i = 0; i &lt; _markers.size(); i++) {
            ReadableMap marker = _markers.getMap(i);
            SimpleMarkerBalloon balloon = new SimpleMarkerBalloon(marker.getString(&quot;label&quot;));
            LatLng markerCenter = new LatLng(marker.getDouble(&quot;lat&quot;), marker.getDouble(&quot;lng&quot;));
            _tomTomMap.addMarker(new MarkerBuilder(markerCenter).markerBalloon(balloon));
        }
    }

    protected void reactNativeEvent(String eventName, WritableMap eventParams) {
        ReactContext reactContext = (ReactContext) this.getContext();
        reactContext
                .getJSModule(RCTEventEmitter.class)
                .receiveEvent(this.getId(), eventName, eventParams);
    }
}


</code></pre>
<p>Here, the most important part is the way we implement "onMapReady" delegate. It is an asynchronous process. So, be careful. Because the initial props will be set before the map is ready. Therefore, we used class variables and used them later on when the map became ready. But, onMapReady is called only when map is created or resumed (when app comes from background). But, props could be changed from JavaScript at anytime. For example; setMapZoom will be called at anytime to zoom prop changed in JavaScript. So, if the map is already available; we invoke the relevant functions to manipulate the map.</p><figure class="kg-card kg-image-card"><img src="https://cuneyt.aliustaoglu.biz/en/content/images/2019/08/ReactNativeTomTom.png" class="kg-image" alt="Using TomTom Maps in React Native: Part 1 - Android"><figcaption>Fully functional TomTom map in React Native</figcaption></figure><p>Now try to change the props in JavaScript side by adding some buttons on the screen. Add your hometown and neighbour city for instance :)</p><p>To implement the equivalent solution in IOS. Go ahead for Part 2:</p><p><a href="https://cuneyt.aliustaoglu.biz/en/using-tomtom-maps-in-react-native-part-2-ios/">https://cuneyt.aliustaoglu.biz/en/using-tomtom-maps-in-react-native-part-2-ios/</a></p>]]></content:encoded></item><item><title><![CDATA[Setting up a Cordova application with React & Webpack & Babel from scratch]]></title><description><![CDATA[We will build a minimal React & Webpack & Babel application in Cordova so you can build Android, IOS, Windows etc. applications.]]></description><link>https://cuneyt.aliustaoglu.biz/en/setting-up-a-cordova-react-application-from-stratch/</link><guid isPermaLink="false">5d45f02e250093415b920654</guid><category><![CDATA[Cordova]]></category><category><![CDATA[React]]></category><dc:creator><![CDATA[Cuneyt Aliustaoglu]]></dc:creator><pubDate>Sat, 03 Aug 2019 21:58:51 GMT</pubDate><media:content url="https://cuneyt.aliustaoglu.biz/en/content/images/2019/08/banner-cordova-react.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://cuneyt.aliustaoglu.biz/en/content/images/2019/08/banner-cordova-react.jpg" alt="Setting up a Cordova application with React & Webpack & Babel from scratch"><p>I generally use React Native to build mobile applications. But I also like how easy it is to setup multi platform apps using Cordova. With React Native you're limited to react native libraries, however with Cordova you can pretty much use any npm library. Anyway this tutorial is not about comparison. It's how to setup a minimal React &amp; Webpack &amp; Babel application in Cordova so you can build Android, IOS, Windows etc. applications.</p><p>The boilerplate that I am going to build now could be found here:</p><p><a href="https://github.com/aliustaoglu/cordova-react-starter">https://github.com/aliustaoglu/cordova-react-starter</a></p><p>Install cordova as global if you haven't already. Then create a new app and add Android as platform. Instead of com.cordova.react you can change it to the Android package name that you would like to create.</p><pre><code class="language-sh">npm install -g cordova
cordova create cordova-react-starter com.cordova.react
cd cordova-react-starter
cordova platform add android

</code></pre>
<p>Now let's add some dependencies. Of course later on we can add redux and all other libraries that you use with React. But we will keep it minimal.</p><pre><code class="language-sh">yarn add react react-dom
yarn add -D @babel/core babel-loader @babel/preset-env @babel/preset-react
yarn add -D webpack webpack-cli
</code></pre>
<p>Add cordova webpack plugin:</p><pre><code class="language-sh">cordova plugin add cordova-plugin-webpack
</code></pre>
<p>Add a new webpack configuration</p><pre><code class="language-js">const path = require('path')

module.exports = {
  mode: 'development',
  entry: './src/index.js',
  output: {
    path: path.resolve(__dirname, 'www'),
    filename: 'index.bundle.js'
  },
  devtool: 'inline-source-map',
  module: {
    rules: [
      {
        test: /\.(js|jsx)$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader'
        }
      }
    ]
  }
}

</code></pre>
<p>And create .babelrc file</p><pre><code class="language-json">{ &quot;presets&quot;: [&quot;@babel/preset-env&quot;, &quot;@babel/preset-react&quot;] }
</code></pre>
<p>Run prepare and build commands</p><pre><code class="language-sh">cordova prepare
cordova build -- --webpackConfig webpack.config.js
</code></pre>
<p>This will add index.bundle.js into your www folder. This is a generated bundle file so better if you add this to your gitignore file. </p><p>Now let's do some cleaning. Remove all bloat from css, index and css files:</p><p>First index.html file. Update it as below:</p><pre><code class="language-html">&lt;!DOCTYPE html&gt;

&lt;html&gt;
    &lt;head&gt;
        &lt;meta http-equiv=&quot;Content-Security-Policy&quot; content=&quot;default-src 'self' data: gap: https://ssl.gstatic.com 'unsafe-eval'; style-src 'self' 'unsafe-inline'; media-src *; img-src 'self' data: content:;&quot;&gt;
        &lt;meta name=&quot;format-detection&quot; content=&quot;telephone=no&quot;&gt;
        &lt;meta name=&quot;msapplication-tap-highlight&quot; content=&quot;no&quot;&gt;
        &lt;meta name=&quot;viewport&quot; content=&quot;initial-scale=1, width=device-width, viewport-fit=cover&quot;&gt;
        &lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;css/index.css&quot;&gt;
        &lt;title&gt;Hello World&lt;/title&gt;
    &lt;/head&gt;
    &lt;body&gt;
        &lt;div id=&quot;app&quot;&gt;

        &lt;/div&gt;
        &lt;script type=&quot;text/javascript&quot; src=&quot;cordova.js&quot;&gt;&lt;/script&gt;
        &lt;script type=&quot;text/javascript&quot; src=&quot;index.bundle.js&quot;&gt;&lt;/script&gt;
    &lt;/body&gt;
&lt;/html&gt;

</code></pre>
<p>Note that I've removed "js/index.js" file and instead, placed the bundle that webpack generates. But we need the content of js/index.js. So copy the content of this file and paste into "src/index.js" (the source for the webpack). But we still need to modify some stuff. So, update this file as below and notice how we mount the React App when the device is ready. After this App.js will manage the whole content.</p><pre><code class="language-js">import React from 'react'
import ReactDOM from 'react-dom'
import App from './App';

var app = {
  // Application Constructor
  initialize: function() {
      document.addEventListener('deviceready', this.onDeviceReady.bind(this), false);
  },

  // deviceready Event Handler
  //
  // Bind any cordova events here. Common events are:
  // 'pause', 'resume', etc.
  onDeviceReady: function() {
      ReactDOM.render(&lt;App /&gt;, document.getElementById('app'))
  },
};

app.initialize();
</code></pre>
<p>Clear "www/css/index.css" file too. And here's the App.js content:</p><pre><code class="language-js">import React from 'react'

const App = () =&gt; {
  return &lt;div style={{ display: 'flex', justifyContent: 'center' }}&gt;Hello from React&lt;/div&gt;
}

export default App

</code></pre>
<p>My directory structure is like this:</p><pre><code class="language-txt">├── hooks
├── node_modules
├── platforms
│   └── android
├── plugins
│   ├── cordova-plugin-webpack
│   └── cordova-plugin-whitelist
├── src
│   ├── App.js
│   └── index.js
├── www
│   ├── index.bundle.js
│   └── index.html
├── .babelrc
├── config.xml
├── package.json
└── webpack.config.js
</code></pre>
<p>Now run below commands to deploy the app.</p><pre><code class="language-sh">cordova build -- --webpackConfig webpack.config.js
cordova run android 
</code></pre>
<p>To enable livereload run below. So, when you make a change you don't need to create the bundle file and upload apk again.</p><pre><code class="language-sh">cordova run -- --livereload
</code></pre>
<p>Minimal React application is mounted inside Cordova:</p><figure class="kg-card kg-image-card"><img src="https://cuneyt.aliustaoglu.biz/en/content/images/2019/08/hello-react-cordova.png" class="kg-image" alt="Setting up a Cordova application with React & Webpack & Babel from scratch"></figure>]]></content:encoded></item><item><title><![CDATA[Hide your API keys from AndroidManifest.xml using local.properties]]></title><description><![CDATA[You wouldn't want to check-in your API keys to the source code. Instead, you would like to embed them into your application generated by build tools using an external file that is ignored by your source controller.]]></description><link>https://cuneyt.aliustaoglu.biz/en/hide-your-api-keys-from-androidmanifest-xml-using-local-properties/</link><guid isPermaLink="false">5d217bd8250093415b92064b</guid><category><![CDATA[Android]]></category><category><![CDATA[React Native]]></category><dc:creator><![CDATA[Cuneyt Aliustaoglu]]></dc:creator><pubDate>Sun, 07 Jul 2019 05:24:39 GMT</pubDate><media:content url="https://cuneyt.aliustaoglu.biz/en/content/images/2019/07/Title.png" medium="image"/><content:encoded><![CDATA[<img src="https://cuneyt.aliustaoglu.biz/en/content/images/2019/07/Title.png" alt="Hide your API keys from AndroidManifest.xml using local.properties"><p>You wouldn't want to check-in your API keys to the source code. Instead, you would like to embed them into your application generated by build tools using an external file that is ignored by your source controller. For Android there's already a standard file that's used for this purpose called local.properties.</p><p>AndroidManifest.xml is an xml file obviously and we cannot write scripts there. Instead, we can write a small script in build.gradle file, read api key from local.properties and assign it to a variable. AndroidManifest can read this variable from gradle file.</p><p>Let's say we want to add Google Maps API Key:</p><p>AndroidManifest.xml</p>
<pre><code class="language-xml">&lt;meta-data android:name=&quot;com.google.android.geo.API_KEY&quot; android:value=&quot;${googleMapApiKey}&quot;/&gt;
</code></pre>
<p>googleMapApiKey is the variable that I want to create in build.gradle. Add below lines right before android{} section in your build.gradle(Module: app)</p><p>build.gradle</p>
<pre><code class="language-groovy">Properties properties = new Properties()
properties.load(project.rootProject.file('local.properties').newDataInputStream())
def googleMapApiKey = properties.getProperty('google.map.key')

android {
}
</code></pre>
<p>This will look for "google.map.key" variable in local.properties file. So add your API key as below in local.properties file.</p><p>local.properties</p>
<pre><code>## This file must *NOT* be checked into Version Control Systems,
# as it contains information specific to your local configuration.
#
# Location of the SDK. This is only used by Gradle.
# For customization when using a Version Control System, please read the
# header note.
google.map.key = AIzaS*******-**********-*******
</code></pre>
<p>Sync your project with Gradle files and it should be all fine now. </p><h3 id="usingenvironmentvariables">Using Environment Variables</h3>
<p>But you need to keep in mind that local.properties will not be checked into your source code. So, if you're using CI/CD (eg. appcenter), it will fail. local.properties is only good when you're a sole developer, generating apk in your local environment and uploading it manually.</p><p>But if you're in a team and using CI/CD the best way would be to use environment variables. Your AndroidManifest.xml will be the same</p><p>AndroidManifest.xml</p>
<pre><code class="language-xml">&lt;meta-data android:name=&quot;com.google.android.geo.API_KEY&quot; android:value=&quot;${googleMapApiKey}&quot;/&gt;
</code></pre>
<p>But gradle.build will be like below:</p><p>build.gradle</p>
<pre><code class="language-groovy">def googleMapApiKey = System.getenv(&quot;googleMapApiKey&quot;)

android {
}
</code></pre>
<p>Now we need to set the environment variables. Open up your bash_profile file and </p><pre><code class="language-sh">nano ~/.bash_profile
</code></pre>
<p>And paste your credentials.</p><pre><code class="language-sh">export googleMapApiKey=AIzxxxxx-xxxxx-xxxx
</code></pre>
<p>It is important that you need to add this into ~/.bash_profile even if you're using zsh or other terminals. In my machine gradle sync did not read zsh config.</p><p>For Windows instead of export, you need to use SET  command</p><pre><code class="language-sh">set googleMapApiKey=AIzxxxxx-xxxxx-xxxx
</code></pre>
]]></content:encoded></item><item><title><![CDATA[Using Native UI Libraries in React Native with Swift]]></title><description><![CDATA[React Native uses Objective-C by default for native libraries. This tutorial covers how to use Swift for UI libraries in IOS using Cocoapods.]]></description><link>https://cuneyt.aliustaoglu.biz/en/using-native-ui-libraries-in-react-native-for-with-swift/</link><guid isPermaLink="false">5d03624c250093415b92063e</guid><category><![CDATA[React Native]]></category><category><![CDATA[Swift]]></category><dc:creator><![CDATA[Cuneyt Aliustaoglu]]></dc:creator><pubDate>Fri, 14 Jun 2019 09:41:10 GMT</pubDate><media:content url="https://cuneyt.aliustaoglu.biz/en/content/images/2019/06/ios-swift-cocoapods.jpeg" medium="image"/><content:encoded><![CDATA[<img src="https://cuneyt.aliustaoglu.biz/en/content/images/2019/06/ios-swift-cocoapods.jpeg" alt="Using Native UI Libraries in React Native with Swift"><p>React Native uses Objective-C by default for native libraries. This tutorial covers how to use Swift for UI libraries in IOS using Cocoapods.</p><p>I made this tutorial for a demo I've presented in a RN meetup group in Auckland. Also wanted to share here:</p><figure class="kg-card kg-embed-card"><style type="text/css">
#meetup_oembed .mu_clearfix:after { visibility: hidden; display: block; font-size: 0; content: " "; clear: both; height: 0; }
* html #meetup_oembed .mu_clearfix, *:first-child+html #meetup_oembed .mu_clearfix { zoom: 1; }
#meetup_oembed { background:#eee;border:1px solid #ccc;padding:10px;-moz-border-radius:3px;-webkit-border-radius:3px;border-radius:3px;margin:0; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 12px; }
#meetup_oembed h3 { font-weight:normal; margin:0 0 10px; padding:0; line-height:26px; font-family:Georgia,Palatino,serif; font-size:24px }
#meetup_oembed p { margin: 0 0 10px; padding:0; line-height:16px; }
#meetup_oembed img { border:none; margin:0; padding:0; }
#meetup_oembed a, #meetup_oembed a:visited, #meetup_oembed a:link { color: #1B76B3; text-decoration: none; cursor: hand; cursor: pointer; }
#meetup_oembed a:hover { color: #1B76B3; text-decoration: underline; }
#meetup_oembed a.mu_button { font-size:14px; -moz-border-radius:3px;-webkit-border-radius:3px;border-radius:3px;border:2px solid #A7241D;color:white!important;text-decoration:none;background-color: #CA3E47; background-image: -moz-linear-gradient(top, #ca3e47, #a8252e); background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #a8252e), color-stop(1, #ca3e47));disvplay:inline-block;padding:5px 10px; }
#meetup_oembed a.mu_button:hover { color: #fff!important; text-decoration: none; }
#meetup_oembed .photo { width:50px; height:50px; overflow:hidden;background:#ccc;float:left;margin:0 5px 0 0;text-align:center;padding:1px; }
#meetup_oembed .photo img { height:50px }
#meetup_oembed .number { font-size:18px; }
#meetup_oembed .thing { text-transform: uppercase; color: #555; }
</style>
<div id="meetup_oembed" style="height:392px">
     <div style="max-height:352px;overflow:hidden">
          <h3>Using Native iOS Libraries in React Native</h3>
          <p style="margin:5px 0;font-size:16px">Thursday, Jun 13, 2019,  6:30 PM</p>
          <p style="margin: 0 0 5px;"><span style="font-size:14px">Vista Entertainment Solutions</span><br>
<span style="font-size:12px;">60 Khyber pass road Auckland, NZ</span></p>

          <span style="color:#4F8A10;font-size:16px;">34 Reactors Went</span> 
          <div style="margin:5px 0 10px" class="mu_clearfix">
               <div class="photo"><img src="https://secure.meetupstatic.com/photos/member/5/3/0/d/thumb_277821261.jpeg" alt="Using Native UI Libraries in React Native with Swift"></div><div class="photo"><img src="https://secure.meetupstatic.com/photos/member/6/7/6/4/thumb_285266468.jpeg" alt="Using Native UI Libraries in React Native with Swift"></div><div class="photo"><img src="https://secure.meetupstatic.com/photos/member/2/7/7/d/thumb_285850109.jpeg" alt="Using Native UI Libraries in React Native with Swift"></div><div class="photo"><img src="https://secure.meetupstatic.com/photos/member/8/2/7/thumb_251882087.jpeg" alt="Using Native UI Libraries in React Native with Swift"></div><div class="photo"><img src="https://secure.meetupstatic.com/photos/member/a/3/7/a/thumb_280541850.jpeg" alt="Using Native UI Libraries in React Native with Swift"></div>
          </div>
          <p style="line-height:16px">Our guest speaker Cuneyt will show us how to implement a simple video player in iOS with native libraries and use it in a React Native app. In this meetup, you will learn how to build your own native library from cocoapods and plug it into a React Native app. This is a very useful technique to using out of the box native libraries without depending...</p>
     </div>
     <p style="margin:10px 0 0;"><a href="https://www.meetup.com/Auckland-React-Native-Meetup/events/260984121/" target="_blank" class="mu_button"><strong>Check out this Meetup &rarr;</strong></a></p>
</div>
</figure><p>This application uses CocoaPods</p><p>Please refer to CocoaPods to make the installation:<br><a href="https://guides.cocoapods.org/using/getting-started.html">https://guides.cocoapods.org/using/getting-started.html</a></p><p>After you've installed Cocoapods to your machine, we need to set it up for our project.</p><pre><code>cd ios
pod init
pod install
</code></pre><p>You may get below error. This is a bug and it is actually declared twice. Just remove one of the instances from <code>ios/Podfile</code>. Then re-run pod install.</p><pre><code>$ pod install
Analyzing dependencies
[!] The target `rnAKLMeetupDemo-tvOSTests` is declared multiple times.
</code></pre><p>We won't even be using tvOS, so I can remove that one too. My pod file will look like below:</p><pre><code class="language-ruby"># Uncomment the next line to define a global platform for your project
platform :ios, '9.0'

target 'rnAKLMeetupDemo' do
  # Comment the next line if you don't want to use dynamic frameworks
  # use_frameworks!

  # Pods for rnAKLMeetupDemo

  # pod &quot;PlayerKit&quot; // I will add PlayerKit later ;)

end

</code></pre>
<p>After this I can run pod install again:</p><figure class="kg-card kg-image-card"><img src="https://raw.githubusercontent.com/aliustaoglu/react-native-auckland-meetup-demo/master/pics/pod-install.png" class="kg-image" alt="Using Native UI Libraries in React Native with Swift"><figcaption>`</figcaption></figure><p>As shown in the picture above, we don't have any dependencies yet.</p><p>I want to implement Vimeo's PlayerKit component:</p><p><a href="https://cocoapods.org/pods/PlayerKit">https://cocoapods.org/pods/PlayerKit</a></p><p>Let's add below to <code>ios/Podfile</code> under the line <code># Pods for rnAKLMeetupDemo</code></p><blockquote>pod "PlayerKit"</blockquote><p>and run <code>pod install again</code></p><p>It should print below message this time</p><pre><code>Pod installation complete! There is 1 dependency from the Podfile and 1 total pod installed.
</code></pre><p>Now if you open Xcode, under Pods you will see PlayerKit. Run the application to see the classic "Welcome to React Native" message, so we know that everything compiles.</p><p>Under rnAKLMeetupDemo:</p><ol><li>Create NativeModules directory (we don't have to but I want to be a bit tidy)</li><li>Add a new Swift file called CustomPlayer</li><li>Xcode will ask you to create a bridging header. Say yes to it. You can change the name but default name is also OK. I used the default name</li><li>Bridging header will only be created once. Next time you want to add another Swift file you will not need and should not create another bridging header.</li></ol><figure class="kg-card kg-image-card"><img src="https://raw.githubusercontent.com/aliustaoglu/react-native-auckland-meetup-demo/master/pics/add-swift-and-bridge.gif" class="kg-image" alt="Using Native UI Libraries in React Native with Swift"></figure><p>React Native does not work with Swift-only setup yet. So, we need to create Objective-C files just to export our Swift files. Don't worry, Objective C files will be minimal. You can think them as interfaces to the implementation we do in Swift.</p><p>Under the same folder (NativeModules), this time create an Objective-C file called</p><figure class="kg-card kg-image-card"><img src="https://raw.githubusercontent.com/aliustaoglu/react-native-auckland-meetup-demo/master/pics/create-objective-c.png" class="kg-image" alt="Using Native UI Libraries in React Native with Swift"></figure><figure class="kg-card kg-image-card"><img src="https://raw.githubusercontent.com/aliustaoglu/react-native-auckland-meetup-demo/master/pics/customplayer.m.png" class="kg-image" alt="Using Native UI Libraries in React Native with Swift"></figure><p>Now we have 3 files:</p><ol><li>CustomPlayer.swift</li><li>rnAKLMeetupDemo-Bridging-Header.h</li><li>CustomPlayer.m</li></ol><p>Let's first update bridging header with the below imports that is needed by Swift file</p><pre><code class="language-objectivec">#import &quot;React/RCTBridgeModule.h&quot;
#import &quot;React/RCTViewManager.h&quot;

</code></pre>
<p>Then update CustomPlayer.swift to use PlayerKit</p><pre><code class="language-swift">import Foundation
import UIKit
import PlayerKit
import AVFoundation

@objc(CustomPlayer)
class CustomPlayer: RCTViewManager {
  
  override func view() -&gt; UIView! {
    let player = RegularPlayer()
    player.set(AVURLAsset(url: URL.init(string: &quot;http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4&quot;)!))
    return player.view
  }
  
  override static func requiresMainQueueSetup() -&gt; Bool {
    return true
  }
}


</code></pre>
<p>We need to create a class that extends RCTViewManager. This way we tell React Native that we will manage UI from the native side. JavaScript will decide when and where to render this control.</p><p>We use @objc decorator for any class, method or property we want to export to JavaScript. But before going to JavaScript, it needs to go to Objective-C first.</p><p>Now open CustomPlayer.m to see how we export Swift class to JavaScript:</p><pre><code class="language-objectivec">#import &lt;Foundation/Foundation.h&gt;
#import &quot;React/RCTViewManager.h&quot;

@interface RCT_EXTERN_MODULE(CustomPlayer, RCTViewManager)

@end


</code></pre>
<p>Here we just exported the module. You may have seen <code>RCT_EXPORT_MODULE</code> macro. That is when you use Objective-C modules only. If the module you're exporting from is a Swift class, you need to use RCT_EXPORT_MODULE instead.</p><p>So, that is it from the native side. Pretty tiny. But you can see that we only exported the module. We haven't exported or implemented any methods (pause, play, ff, rw etc.) But first I just want to see if this works:</p><p>Now go to App.js (of course in real life you may wanna use another file but for simplicity we use App.js)</p><pre><code class="language-js">import React, { Component } from 'react';
import { StyleSheet, Text, View, requireNativeComponent } from 'react-native';

const CustomPlayer = requireNativeComponent('CustomPlayer')

export default class App extends Component {
  render() {
    return (
      &lt;View style={styles.container}&gt;
        &lt;Text&gt;Welcome to React Native!&lt;/Text&gt;
        &lt;CustomPlayer style={{ width: 300, height: 300 }} /&gt;
      &lt;/View&gt;
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#F5FCFF'
  }
});

</code></pre>
<p>With requireNativeComponent, we import the UIView class from the Swift thru Objective-C. Name of the component should be what you use in the brackets of the decorator <code>objc(CustomPlayer)</code>.</p><p>We need to specify height and width to see the video player.</p><figure class="kg-card kg-image-card"><img src="https://raw.githubusercontent.com/aliustaoglu/react-native-auckland-meetup-demo/master/pics/custom-player.png" class="kg-image" alt="Using Native UI Libraries in React Native with Swift"></figure><p>It works!</p><p>But video does not play because we did not implement anything about it. If you add player.play() before you return the view, you see it automatically plays the video.</p><pre><code class="language-swift">    player.play()
    return player.view

</code></pre>
<p>But let's do this by using a prop so user can decide whether the video should autoplay or not. And also let's not hardcode filename.</p><pre><code class="language-jsx">&lt;CustomPlayer 
  autoPlay={true} 
  filename=&quot;http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4&quot;
  style={{ width: 300, height: 300 }} /&gt;

</code></pre>
<p>Before doing that, let's separate our View logic from ViewManager.</p><p>Add a file named PlayerView.swift next to your ViewManager.</p><pre><code class="language-swift">import Foundation
import UIKit

class PlayerView: UIView {
  
  
  override init(frame:CGRect) {
    super.init(frame: frame)
    self.addSubview(UIView())
  }
  
  required init?(coder aDecoder: NSCoder) {
    fatalError(&quot;init(coder:) has not been implemented&quot;)
  }
    
}

</code></pre>
<p>and update CustomPlayer.swift file as below:</p><pre><code class="language-swift">@objc(CustomPlayer)
class CustomPlayer: RCTViewManager {

  
  override func view() -&gt; UIView! {
    return PlayerView(frame: CGRect(x:0, y:0, width: 300, height: 300))
  }
  
  override static func requiresMainQueueSetup() -&gt; Bool {
    return true
  }
}


</code></pre>
<p>Now we have a view that does not do anything. Just ignore <code>required init?</code> bit and take it as it is now.</p><p>Let's change PlayerView as below</p><pre><code class="language-swift">  import PlayerKit
  import AVFoundation
  ...
  override init(frame:CGRect) {
    super.init(frame: frame)
    player = RegularPlayer()
    player.set(AVURLAsset(url: URL.init(string: &quot;http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4&quot;)!))
    player.play()
    player.view.frame = frame
    self.addSubview(player.view)
  }


</code></pre>
<p>Now, PlayerView is ready to be called from JavaScript. So, let's update JavaScript as below:</p><pre><code class="language-js">
import { requireNativeComponent } from 'react-native';

...

&lt;CustomPlayer style={{ width: 300, height: 300 }} /&gt;


</code></pre>
<p>Now run the application again. Everything is hard coded. But works smoothly.</p><p>Now let's add autoPlay and filename props to the React element and manage it from the native side.</p><p>To export the prop we use RCT_EXPORT_VIEW_MODULE macro in the Objective-C.</p><pre><code class="language-objectivec">#import &lt;Foundation/Foundation.h&gt;
#import &quot;React/RCTViewManager.h&quot;

@interface RCT_EXTERN_MODULE(CustomPlayer, RCTViewManager)
  RCT_EXPORT_VIEW_PROPERTY(autoPlay, BOOL)
  RCT_EXPORT_VIEW_PROPERTY(filename, NSString)
@end


</code></pre>
<p>Now where to add autoPlay and filename to Swift?</p><p>Update your PlayerView.swift file as below and pay attention to autoPlay and filename variables. We added @objc decorator to sign them as exportable to Objective-C and then JavaScript.</p><p>Also setters work asyncronously, so you cannot use these props in the init() function. When the props are set, didSetProps() function is called. It is called with changedProps parameters which include an array of prop names. This was also one of my reasons to separate UIView logic from RCTViewManager. I can easily override lifecycle functions and have tidier components.</p><pre><code class="language-swift">import Foundation
import UIKit
import PlayerKit
import AVFoundation

class PlayerView: UIView {
  
  @objc var autoPlay: Bool = false
  @objc var filename: NSString = &quot;&quot;
  var player: RegularPlayer!
  
  override init(frame:CGRect) {
    super.init(frame: frame)
    player = RegularPlayer()
    player.view.frame = frame
    self.addSubview(player.view)
  }
  
  override func didSetProps(_ changedProps: [String]!) {
    player.set(AVURLAsset(url: URL.init(string: self.filename as String)!))
    if (self.autoPlay == true){
      player.play()
    }
  }
  
  required init?(coder aDecoder: NSCoder) {
    fatalError(&quot;init(coder:) has not been implemented&quot;)
  }
  
}


</code></pre>
<p>And our javascript file is not hard to guess:</p><pre><code class="language-jsx">
&lt;CustomPlayer 
  autoPlay={true}
  filename=&quot;http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4&quot;
  style={{ width: 300, height: 300 }} /&gt;

</code></pre>
<p>So, the parameters are no longer hard-coded in the native side.</p><p>autoPlay prop is very useful but not enough. I want to keep it but also want to control the video manually. I want to be able to do this by 2 ways.</p><ol><li>By clicking on the video</li><li>By clicking on a  that was created in the JavaScript. </li></ol><p>First one is easy. I will just override tochesBegan (or end) function of my view.</p><p>Update PlayerView.swift as below. And the code is self explanatory.</p><pre><code class="language-swift">
  override func touchesBegan(_ touches: Set&lt;UITouch&gt;, with event: UIEvent?) {
    if (self.player.playing) {
      self.player.pause()
    } else {
      self.player.play()
    }
  }

</code></pre>
<p>For the second method, we need to send a command from JS to Native. Things will get a little bit complicated. We need to get the UIView's node handle number to send commands. To get the "node handle" we need to use "ref". And then using UIManager's command dispatcher. Let's add some new classes from react-native library.</p><p>I will implement the play/pause functionality again, but this time from JavaScript button. JS will send a command to Swift and UIViewManager of IOS will respond to my JS command.</p><pre><code class="language-js">import { ... UIManager, findNodeHandle, createRef } from 'react-native'
const playerRef = React.createRef();
...
render(){
  ...
    &lt;CustomPlayer ref={playerRef} ... &gt;&lt;/CustomPlayer&gt;
    &lt;Button
      title=&quot;Toggle play&quot;
      onPress={() =&gt; {
        const nativeCommands = UIManager.getViewManagerConfig('CustomPlayer').Commands;
        const playerNodeHandle = findNodeHandle(playerRef.current);
        UIManager.dispatchViewManagerCommand(playerNodeHandle, nativeCommands.togglePlay, []);
      }}
    /&gt;
}

</code></pre>
<ul><li>First we created a ref and added as ref prop of CustomPlayer.</li><li>Then get the node handle from the ref.</li><li>Get native commands that are created with @objc in the view manager.</li><li>Using dispatchViewManagerCommand I've triggered a command called togglePlay (which I haven't implemented yet)</li></ul><p>Let's implement this command in Swift:</p><p>update CustomPlayer.swift as below:</p><pre><code class="language-swift">

....
class CustomPlayer: RCTViewManager {
  var playerView: PlayerView!
  
  override func view() -&gt; UIView! {
    playerView = PlayerView(frame: CGRect(x:0, y:0, width: 300, height: 300))
    return playerView
  }
   
  ...  
  
  @objc public func togglePlay(_ node:NSNumber){
    playerView.togglePlay()
  }

  
}


</code></pre>
<p>ViewManager will call the same named function of the PlayerView class.<br>And update PlayerView as below:</p><pre><code class="language-swift">...
  public func togglePlay(){
    if (self.player.playing) {
      self.player.pause()
    } else {
      self.player.play()
    }
  }
...


</code></pre>
<p>Also we need to update objective-c file as below so that the Swift method will be accessible from JS.</p><pre><code class="language-objectivec">...
RCT_EXTERN_METHOD(togglePlay:(nonnull NSNumber *)node)
...

</code></pre>
<p>Works pretty good. But, Objective-C is very weird especially for JS developers. This example does not send any params to Swift. When I first tried Objective-C, it took me some time to figure out how to send multiple parameters using an Objc function. So let's do another example where we can change the filename that PlayerKit plays.</p><pre><code class="language-js">
...
&lt;TextInput
    style={{ width: 300, height: 40, backgroundColor: '#ddd' }}
    value={this.state.videoUrl}
    onChange={e =&gt; this.setState({ videoUrl: e.nativeEvent.text })}
  /&gt;
&lt;Button
    title=&quot;Change video&quot;
    onPress={() =&gt; {
      const playerNodeHandle = findNodeHandle(playerRef.current);
      UIManager.dispatchViewManagerCommand(playerNodeHandle, this.nativeCommands.changeVideo, [
        this.state.videoUrl,
        'dummy param'
      ]);
    }}
/&gt;

...


</code></pre>
<p>This time we invoke a function called changeVideo and send 2 string parameters. One is a new url and the second one is just a dummy parameter just to show how to add more params. We won't use it. Just check togglePlay does not send any params (other than node handle) and changeVideo sends 2 params.</p><pre><code class="language-objectivec">RCT_EXTERN_METHOD(togglePlay:(nonnull NSNumber *)node)
RCT_EXTERN_METHOD(changeVideo:(nonnull NSNumber *)node
                  url:(nonnull NSString *)url
                  extraParam:(nonnull NSString *)extraParam)


</code></pre>
<p>first parameter in the array that we dispatch will be assigned to url and second param will be assigned to extraParam. playerNodeHandle is assigned to node variable that we will not use. But, RN/Swift uses that in the background.</p><p>CustomPlayer.swift</p><pre><code class="language-swift">...
  @objc public func changeVideo(_ node:NSNumber, url:NSString, extraParam:NSString){
    playerView.changeVideo(url: url, extraParam: extraParam)
  }
...

</code></pre>
<p>PlayerView.swift</p><pre><code class="language-swift">  public func changeVideo(url:NSString, extraParam:NSString){
    player.set(AVURLAsset(url: URL.init(string: url as String)!))
    player.play()
  }

</code></pre>
<p>You can use the below gist that I've forked from somewhere else to test this new function with different URLs.</p><p><a href="https://gist.github.com/aliustaoglu/68d5f9a59c83a9b3f116fa8438c6d14c">https://gist.github.com/aliustaoglu/68d5f9a59c83a9b3f116fa8438c6d14c</a></p><p>We need to cover one more thing. Sending a command from Swift to JS. We can do this by using a JS prop. Remember we used <code>autoPlay={true}</code>. What happens if we use prop like this: <code>onVideoFinished={ e =&gt; doSomething() }</code>. Then this function would be invoked from Swift and JS would do anything with the returned params.</p><p>For this better if we could use delegates. PlayerKit implements a delegate called PlayerDelegate. It does not have a function when video finishes but it does have one function as the time changes. So when <code>player.time &gt;= player.duration</code> we can say the video is finished.</p><p>CustomPlayer.swift</p><pre><code class="language-swift">class PlayerView: UIView, PlayerDelegate {
  func playerDidUpdateState(player: Player, previousState: PlayerState) {
    
  }
  
  func playerDidUpdatePlaying(player: Player) {
    
  }
  
  func playerDidUpdateTime(player: Player) {
    if (player.time&gt;=player.duration) {
      self.onVideoFinished!([&quot;message&quot;: &quot;I am finished&quot;, &quot;foo&quot;: &quot;bar&quot;])
    }
  }
  
  func playerDidUpdateBufferedTime(player: Player) {
    
  }

  ...
  @objc var onVideoFinished:RCTDirectEventBlock? = nil
  ...

  init(){
    ...
    player.delegate = self as PlayerDelegate
  }


</code></pre>
<p>CustomPlayer.m</p><pre><code class="language-objectivec">...
RCT_EXPORT_VIEW_PROPERTY(onVideoFinished, RCTDirectEventBlock)
...

</code></pre>
<p>App.js</p><pre><code class="language-js">        &lt;CustomPlayer
          ...
          onVideoFinished={e =&gt; {
            alert(e.nativeEvent.message);
            console.log(e.nativeEvent);
          }}
        /&gt;

</code></pre>
<h1 id="using-ui-native-modules-on-android">Using UI Native Modules on Android</h1><p>I have also made a tutorial about using UI native modules on Android. It's not for a video player though but it does not matter. It's for an application that uses Google Maps. I needed to use lots of markers, but it was extremely slow using the react-native-maps. So, I implemented a solution that uses Google Maps Android SDK. I needed to solve similar problems with Android in this tutorial.</p><p><a href="https://cuneyt.aliustaoglu.biz/en/how-to-improve-google-maps-performance-in-react-native-for-android/">https://cuneyt.aliustaoglu.biz/en/how-to-improve-google-maps-performance-in-react-native-for-android/</a></p>]]></content:encoded></item><item><title><![CDATA[How to Improve Google Maps Performance in React Native for Android]]></title><description><![CDATA[Very famous npm package called "react-native-maps" is easy to use and configure, but it can be remarkably slow when you're working with lots of markers. I noticed this when using an Android device in particular. This is mostly because of React's declarative way of rendering things. ]]></description><link>https://cuneyt.aliustaoglu.biz/en/how-to-improve-google-maps-performance-in-react-native-for-android/</link><guid isPermaLink="false">5cc291d0250093415b92061b</guid><category><![CDATA[React Native]]></category><category><![CDATA[Google Maps]]></category><category><![CDATA[Android]]></category><dc:creator><![CDATA[Cuneyt Aliustaoglu]]></dc:creator><pubDate>Sat, 27 Apr 2019 03:59:51 GMT</pubDate><media:content url="https://cuneyt.aliustaoglu.biz/en/content/images/2019/04/NativeGoogleMaps-4.png" medium="image"/><content:encoded><![CDATA[<img src="https://cuneyt.aliustaoglu.biz/en/content/images/2019/04/NativeGoogleMaps-4.png" alt="How to Improve Google Maps Performance in React Native for Android"><p>Very famous npm package called "react-native-maps" is easy to use and configure, but it can be remarkably slow when you're working with lots of markers. I noticed this when using an Android device in particular. This is mostly because of React's declarative way of rendering things. </p><p>In most npm packages for Google Maps, markers and all other map objects are declared as a React component. As you touch the map, hundreds of React components are being triggered. They may not being re-rendered or mounted, but render() and other lifecycle methods are being called and checked. This causes the extreme lag.</p><p>In my opinion, Google Maps API is not built to be used declarative. It should be used imperative be it JavaScript API or Android SDK. I had the same issues when I used a React wrapper instead of using Javascript API directly. If you need more performance, you should create the map, get reference and create all the objects using the direct methods and let Google API handle the rest.</p><p>Enough talk, let me try to explain how I solved my problems using native code. This tutorial may not be suitable for people who are just starting to use react and/or react-native. </p><ul><li>Create a new application: </li></ul><pre><code class="language-bash">react-native init reactNativeGoogleMapNativeAndroid
cd reactNativeGoogleMapNativeAndroid
yarn install
</code></pre>
<ul><li>Open <code>reactNativeGoogleMapNativeAndroid/android</code> folder using Android Studio</li><li>Add below dependencies to build.gradle (Module:app) file. After adding below lines, gradle will start syncing. </li></ul><pre><code>dependencies {
        implementation 'com.google.maps.android:android-maps-utils:0.5+'
        implementation 'com.google.android.gms:play-services-maps:16.1.0'
        implementation 'com.google.android.gms:play-services-base:16.1.0'
    }
</code></pre>
<ul><li>Add your Google Maps API key into AndroidManifest.xml</li></ul><pre><code class="language-xml">&lt;application&gt;
    &lt;meta-data android:name=&quot;com.google.android.geo.API_KEY&quot; android:value=&quot;YOUR_API_KEY&quot;/&gt;
&lt;/application&gt;
</code></pre>
<ul><li>Below is how my Android Studio will look like after I add java files:</li></ul><figure class="kg-card kg-image-card"><img src="https://cuneyt.aliustaoglu.biz/en/content/images/2019/04/Android-studio-after-java-files.png" class="kg-image" alt="How to Improve Google Maps Performance in React Native for Android"><figcaption>Android Studio folder structure</figcaption></figure><ul><li>Add a new package called NativeModules and create a new Java class that extends SimpleViewManager&lt;MapView&gt;. Name it as `GMap`. This will be our React Component on JavaScript side.</li><li>You will need to overwrite 2 methods(createViewInstance and getName)</li></ul><pre><code class="language-java">package com.reactnativegooglemapnativeandroid.NativeModules;

import com.facebook.react.uimanager.SimpleViewManager;
import com.facebook.react.uimanager.ThemedReactContext;
import com.google.android.gms.maps.MapView;

import javax.annotation.Nonnull;

public class GMap extends SimpleViewManager&lt;MapView&gt; {

    @Nonnull
    @Override
    public String getName() {
        return &quot;GMap&quot;;
    }

    @Nonnull
    @Override
    protected MapView createViewInstance(@Nonnull ThemedReactContext reactContext) {
        MapView view = new MapView(reactContext);
        view.onCreate(null);
        view.onResume();

        return view;
    }
}


</code></pre>
<ul><li>Now, we need to add this Module into a package that extends ReactPackage and add our GMap reference into ViewManager list in createViewManagers method. Below createNativeModules is empty because we did not add a headless native module. View manager is also a native module but it's a specialised one. Create a file called NativePackages and edit it as below. I normally use this module to include every NativeModule or ViewManager I create. So, I add it into root folder of my main package.</li></ul><pre><code class="language-java">package com.reactnativegooglemapnativeandroid;

import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;
import com.reactnativegooglemapnativeandroid.NativeModules.GMap;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

public class NativePackages implements ReactPackage {

    @Override
    public List&lt;ViewManager&gt; createViewManagers(ReactApplicationContext reactContext) {
        return Arrays.&lt;ViewManager&gt;asList(
                new GMap()
        );
    }

    @Override
    public List&lt;NativeModule&gt; createNativeModules(
            ReactApplicationContext reactContext) {
        List&lt;NativeModule&gt; modules = new ArrayList&lt;&gt;();
        return modules;
    }

}
</code></pre>
<ul><li>Now we will add this module MainApplication.java. I only added <code>new NativePackages()</code> part. The rest was previously created by react-native init command.</li></ul><pre><code class="language-java">package com.reactnativegooglemapnativeandroid;

import android.app.Application;

import com.facebook.react.ReactApplication;
import com.facebook.react.ReactNativeHost;
import com.facebook.react.ReactPackage;
import com.facebook.react.shell.MainReactPackage;
import com.facebook.soloader.SoLoader;

import java.util.Arrays;
import java.util.List;

public class MainApplication extends Application implements ReactApplication {

  private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
    @Override
    public boolean getUseDeveloperSupport() {
      return BuildConfig.DEBUG;
    }

    @Override
    protected List&lt;ReactPackage&gt; getPackages() {
      return Arrays.&lt;ReactPackage&gt;asList(
          new MainReactPackage(),
          new NativePackages()
      );
    }

    @Override
    protected String getJSMainModuleName() {
      return &quot;index&quot;;
    }
  };

  @Override
  public ReactNativeHost getReactNativeHost() {
    return mReactNativeHost;
  }

  @Override
  public void onCreate() {
    super.onCreate();
    SoLoader.init(this, /* native exopackage */ false);
  }
}

</code></pre>
<ul><li>Finally, some time for JavaScript. Call your native SimpleViewManager (MapView) from App.js. For simplicity let's remove all content that was created by <code>react-native init</code>, and add below. This will replace whole page with native Google Map.</li></ul><pre><code class="language-javascript">
import React, {Component} from 'react';
import {Platform, StyleSheet, Text, View, requireNativeComponent} from 'react-native';
import Map from './app/Map'

const GMap = requireNativeComponent('GMap')

export default class App extends Component {
  render() {
    return (
      &lt;GMap style={StyleSheet.absoluteFillObject} /&gt;
    );
  }
}

</code></pre>
<p>Start it with your favourite emulator and see it's loading a bare minimum native google map.</p><figure class="kg-card kg-image-card"><img src="https://cuneyt.aliustaoglu.biz/en/content/images/2019/04/bare-minimum-native-google-maps.png" class="kg-image" alt="How to Improve Google Maps Performance in React Native for Android"><figcaption>Bare minimum Google Maps with native code in Android</figcaption></figure><hr><p>We are not done yet. Now let's see how we can add markers to this map. I will fetch a json file in JS side, and will send it to a native method to render. Let's find some sample data. I have found this perfect example (forked from GitHub user Miserlou). Top 1000 most populated cities in USA. We will see if the natively implemented google maps has a good performance. </p><p><a href="https://gist.githubusercontent.com/aliustaoglu/4a8b2d0bef679023742bbb90c5248eab/raw/2bf258763cdddd704f8ffd3ea9a3e81d25e2c6f6/cities.json">https://gist.githubusercontent.com/aliustaoglu/4a8b2d0bef679023742bbb90c5248eab/raw/2bf258763cdddd704f8ffd3ea9a3e81d25e2c6f6/cities.json</a></p><p>We can add this file into our repo or fetch async. For the sake of simplicity for now I have added it as a file called us_cities.json to my root folder.</p><p>Let's make the changes to our GMap class to handle adding markers which are fetched in JS side of react-native. MapView has a method called <code>getMapAsync()</code>. As the name suggests it's an asynchronous method that gets the map instance as a GoogleMap object. With this instance we can use everything that Android SDK has to offer. </p><p>Let's extend our java class to implement interface called OnMapReadyCallback:</p><blockquote>public class GMap extends SimpleViewManager&lt;MapView&gt; implements <strong>OnMapReadyCallback</strong> {}</blockquote><p>And change the createViewInstance method to get the map instance.</p><p>Then override the onMapReady method from the interface.</p><p>Also add class variables for GoogleMap, ClusterManager and ReactContext</p><pre><code class="language-java">    private GoogleMap googleMap;
    private ClusterManager mClusterManager;
    private ThemedReactContext context;

    protected MapView createViewInstance(@Nonnull ThemedReactContext reactContext) {
        context = reactContext;
        MapView view = new MapView(reactContext);
        view.onCreate(null);
        view.onResume();
        view.getMapAsync(this);

        return view;
    }

    @Override
    public void onMapReady(GoogleMap gmap) {
        googleMap = gmap;
        mClusterManager = new ClusterManager&lt;MyClusterItem&gt;(context, googleMap);
        sendEvent(context, &quot;onMapReady&quot;, null);
    }
    
    private void sendEvent(ReactContext reactContext, String eventName, @Nullable WritableMap params) {
        reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit(eventName, params);
    }

</code></pre>
<p>When map object finishes loading, it invokes the onMapReady method. Here we are ready to create markers. But we also need to send a signal to JS side of our project. After this signal, JavaScript code can send the markers. We have created <code>sendEvent</code> method that can send any signal that could be listened by addListener method in JS.</p><p>Update App.js file so it looks like this:</p><pre><code class="language-javascript">
import React, {Component} from 'react';
import {DeviceEventEmitter, StyleSheet, requireNativeComponent, UIManager, findNodeHandle} from 'react-native';
import cities from './us_cities.json'

const mapRef = React.createRef()
const GMap = requireNativeComponent('GMap')

export default class App extends Component {
  constructor(props){
    super(props)
    this.state = {}
    this.onMapReady = this.onMapReady.bind(this)
  }

  onMapReady(){
    cities.map((city, i) =&gt; {
      UIManager.dispatchViewManagerCommand(this.mapViewHandle, 0, [
        'marker' + 0,
        city.latitude,
        city.longitude,
        city.city,
        city.state,
        city.population
      ]);
    });
  }

  componentDidMount(){
    this.mapViewHandle = findNodeHandle(mapRef.current);
    DeviceEventEmitter.addListener('onMapReady', this.onMapReady);
  }

  render() {
    return (
      &lt;GMap ref={mapRef} style={StyleSheet.absoluteFillObject} /&gt;
    );
  }
}


</code></pre>
<p>Above "onMapReady" listener is the event we dispatched from Java when the map object was created. This listener needs to be added on DeviceEventEmitter, not on window object. Now the JavaScript knows that the map is ready, we can create markers. </p><p>But, how to trigger a method in Java from JavaScript. Normally when we use a NativeModule we can easily invoke methods that are created with @ReactMethod using NativeModules object of react-native. But MapView is not a NativeModule, it's a ViewManager. receiveCommand should be used.</p><p>Implement receiveCommand of ViewManager (in GMap.java)</p><pre><code class="language-java">    // Receives commands from JavaScript using UIManager.dispatchViewManagerCommand
    @Override
    public void receiveCommand(MapView view, int commandId, @Nullable ReadableArray args) {
        super.receiveCommand(view, commandId, args);
        switch (commandId) {
            case 0:
                addMarker(args); // we havent implemented this yet. Read on..
                break;
        }

    }
</code></pre>
<p>When we send a command to Java, we need to use a unique command id (eg. 0, 1, 2 etc.) Then depending on this id, we can decide what to do. If you have noticed, in JavaScript file we used UIManager's dispatchViewManager method with view handle, an integer and an array. This integer will be the commandId in receiveCommand method. Last parameter will be an array of our custom parameters that we want to send. I want to send marker parameters (eg. marker id, lat, lng, Info Window title and content etc.)</p><pre><code class="language-javascript">UIManager.dispatchViewManagerCommand(this.mapViewHandle, 0, [
        'marker' + 0,
        city.latitude,
        city.longitude,
        city.city,
        city.state,
        city.population
      ]);
</code></pre>
<p>MapView object (Gmap) in Java and the React component (also called GMap) are actually identical. So, to convert the React object into a form that Java method understands, we need to use findNodeHandle method. findNodeHandle accepts object ref. So create a ref for it using React.createRef().</p><p>Finally, let's add addMarker method in Java side</p><pre><code class="language-java">    public void addMarker(@Nullable final ReadableArray args) {
        MyClusterItem ci = new MyClusterItem(
                args.getString(0),
                new LatLng(args.getDouble(1),
                        args.getDouble(2)),
                args.getString(3),
                args.getString(4),
                args.getString(5)
                );
        mClusterManager.addItem(ci);
        mClusterManager.cluster();
    }
</code></pre>
<p>We will be adding lots of markers. So, I always prefer using clusters for this. Let's create a custom cluster item. Create a new file called MyClusterItem.java and edit it as below:</p><pre><code class="language-java">package com.reactnativegooglemapnativeandroid.NativeModules;


import com.google.android.gms.maps.model.LatLng;
import com.google.maps.android.clustering.ClusterItem;

public class MyClusterItem implements ClusterItem {

    private LatLng position;
    private String title;
    private String snippet;
    private String population;
    public String tag;

    public MyClusterItem(String g, LatLng pos, String t, String s, String p){
        tag = g;
        position =pos;
        title = t;
        snippet = s + &quot;\n&quot; + p;
    }

    @Override
    public LatLng getPosition() {
        return position;
    }

    @Override
    public String getTitle() {
        return title;
    }

    @Override
    public String getSnippet() {
        return snippet;
    }

    public String getTag(){
        return tag;
    }
}


</code></pre>
<p>We are ready to run:</p><figure class="kg-card kg-image-card"><img src="https://cuneyt.aliustaoglu.biz/en/content/images/2019/04/rn-maps-clustered-1.png" class="kg-image" alt="How to Improve Google Maps Performance in React Native for Android"><figcaption>Map markers clustered</figcaption></figure><p>But, if you zoom in or click the clusters, you'll notice that clusters are not updated. This is not what we want. We want the cluster manager to respond the zoom and bounds change events dynamically. Ad below lines into onMapReady method</p><pre><code class="language-java">googleMap.setOnCameraIdleListener(mClusterManager);
googleMap.setOnMarkerClickListener(mClusterManager);
googleMap.setOnInfoWindowClickListener(mClusterManager);

</code></pre>
<p>Those will responde to camera &amp; zoom changes and info window click event. If we want to zoom in when we click clusters, we need to implement setOnClusterClickListener of cluster manager. Add below lines into onMapReady method:</p><pre><code class="language-java">mClusterManager.setOnClusterClickListener(new ClusterManager.OnClusterClickListener() {
            @Override
            public boolean onClusterClick(Cluster cluster) {
                LatLng clusterPos = new LatLng(cluster.getPosition().latitude, cluster.getPosition().longitude);
                float newZoom = googleMap.getCameraPosition().zoom + 2;
                if (newZoom &gt; googleMap.getMaxZoomLevel())
                    newZoom = googleMap.getMaxZoomLevel();
                googleMap.animateCamera(CameraUpdateFactory.newLatLngZoom(clusterPos, newZoom));
                return true;
            }
        });

</code></pre>
<p>Now we have a native, ultra fast Google Maps running in a React Native app.</p><img src="https://raw.githubusercontent.com/aliustaoglu/react-native-google-map-native-android/master/clusters.gif" alt="How to Improve Google Maps Performance in React Native for Android"><p>You can find the complete code for this tutorial below. Clone it, edit AndroidManifest.xml file then you can run it:</p><p><a href="https://github.com/aliustaoglu/react-native-google-map-native-android">https://github.com/aliustaoglu/react-native-google-map-native-android</a></p>]]></content:encoded></item><item><title><![CDATA[Using Google Maps as Provider in IOS with React Native]]></title><description><![CDATA[If you use react-native-maps in React Native, by default you'll be using Apple Maps for IOS and Google Maps for Android. If you want to use Google Maps for IOS, you'll need to make some changes. Like most things in XCode, this is can get a bit tricky.]]></description><link>https://cuneyt.aliustaoglu.biz/en/using-google-maps-as-provider-in-ios-with-react-native/</link><guid isPermaLink="false">5c7d022a250093415b920606</guid><category><![CDATA[React Native]]></category><category><![CDATA[Google Maps]]></category><category><![CDATA[IOS]]></category><dc:creator><![CDATA[Cuneyt Aliustaoglu]]></dc:creator><pubDate>Sun, 31 Mar 2019 00:21:47 GMT</pubDate><media:content url="https://cuneyt.aliustaoglu.biz/en/content/images/2019/03/GmapsIOS-3.png" medium="image"/><content:encoded><![CDATA[<img src="https://cuneyt.aliustaoglu.biz/en/content/images/2019/03/GmapsIOS-3.png" alt="Using Google Maps as Provider in IOS with React Native"><p>If you use react-native-maps in React Native, by default you'll be using Apple Maps for IOS and Google Maps for Android. If you want to use Google Maps for IOS, you'll need to make some changes. Like most things in XCode, this is can get a bit tricky.</p><p>This tutorial assumes that you're already using Apple Maps in your IOS part of React Native project. You want to use provider as google.</p><pre><code class="language-js">&lt;MapView
          mapType=&quot;satellite&quot;
          provider=&quot;google&quot;
          style={StyleSheet.absoluteFillObject}
          ...
          /&gt;

</code></pre>
<p>When you make this change, your beautiful map will not render anymore and you'll get below screen:</p><figure class="kg-card kg-image-card"><img src="https://cuneyt.aliustaoglu.biz/en/content/images/2019/03/Screen-Shot-2019-03-04-at-22.12.51.png" class="kg-image" alt="Using Google Maps as Provider in IOS with React Native"><figcaption>AirGoogleMaps dir must be added to your XCode project</figcaption></figure><blockquote>console.error: "react-native-maps: AirGoogleMaps dir must be added to your XCode project to support GoogleMaps on iOS"</blockquote><p>First of all add a line `pod 'GoogleMaps'` to your Podfile. Below is my whole podfile:</p><pre><code># File contents of &quot;ios/Podfile&quot;
platform :ios, '9.0'
 
target 'worldsurfforecast' do
  pod 'React', :path =&gt; '../node_modules/react-native', :subspecs =&gt; [
    'Core',
    'CxxBridge',
    'DevSupport',
    # the following ones are the ones taken from &quot;Libraries&quot; in Xcode:
    'RCTAnimation',
    'RCTActionSheet',
    'RCTBlob',
    'RCTGeolocation',
    'RCTImage',
    'RCTLinkingIOS',
    'RCTNetwork',
    'RCTSettings',
    'RCTText',
    'RCTVibration',
    'RCTWebSocket'
  ]
 
  pod 'GoogleMaps' #ADD THIS LINE TO YOUR CODE IF YOU WANT GOOGLEMAPS IN IOS

  # the following dependencies are dependencies of React native itself.
  pod 'yoga', :path =&gt; '../node_modules/react-native/ReactCommon/yoga/Yoga.podspec'
  pod 'DoubleConversion', :podspec =&gt; '../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec'
  pod 'Folly', :podspec =&gt; '../node_modules/react-native/third-party-podspecs/Folly.podspec'
  pod 'glog', :podspec =&gt; '../node_modules/react-native/third-party-podspecs/GLog.podspec'
 
  # your other libraries will follow here!
end
 
# The following is needed to ensure the &quot;archive&quot; step works in XCode.
# It removes React from the Pods project, as it is already included in the main project.
post_install do |installer|
  installer.pods_project.targets.each do |target|
    if target.name == &quot;React&quot;
      target.remove_from_project
    end
  end
end
</code></pre>
<p>To be sure that pods are installed correctly run below commands:</p><pre><code class="language-sh">cd ios
rm -rf Pods/
pod install
</code></pre>
<p>Now how to add the folder to XCode?</p><p>Navigate to <code>node_modules/react-native-maps/lib/ios</code> and find the folder <code>AirGoogleMaps</code>. Move the folder under your &lt;ProjectName&gt;. Choose the option "Create Groups" when moving:</p><figure class="kg-card kg-image-card"><img src="https://cuneyt.aliustaoglu.biz/en/content/images/2019/03/Screen-Shot-2019-03-05-at-00.08.34.png" class="kg-image" alt="Using Google Maps as Provider in IOS with React Native"></figure><p>So you'll see "AirGoogleMaps" folder with the same colour as the rest of the folders.</p><figure class="kg-card kg-image-card"><img src="https://cuneyt.aliustaoglu.biz/en/content/images/2019/03/Screen-Shot-2019-03-05-at-00.11.09.png" class="kg-image" alt="Using Google Maps as Provider in IOS with React Native"></figure><p>Also we need to provide API key to the native code for IOS. Find file <code>AppDelegate.m</code> and make below changes:</p><p>Add line <code>@import GoogleMaps;</code> before <code>@implementation AppDelegate</code> </p><p>Add line <code>[GMSServices provideAPIKey:@"YOUR_API_KEY"];</code> after <code>NSURL *jsCodeLocation;</code></p><p>So, it will look like below:</p><pre><code class="language-objectivec">
/**
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

#import &quot;AppDelegate.h&quot;

#import &lt;React/RCTBundleURLProvider.h&gt;
#import &lt;React/RCTRootView.h&gt;

@import GoogleMaps;
@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
  NSURL *jsCodeLocation;
  [GMSServices provideAPIKey:@&quot;AIzaSyDbTO1Zu-zBBmX9hgk-uUpZz001i-V3Ffo&quot;];
  jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@&quot;index&quot; fallbackResource:nil];

  RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation
                                                      moduleName:@&quot;worldsurfforecast&quot;
                                               initialProperties:nil
                                                   launchOptions:launchOptions];
  rootView.backgroundColor = [UIColor blackColor];

  self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
  UIViewController *rootViewController = [UIViewController new];
  rootViewController.view = rootView;
  self.window.rootViewController = rootViewController;
  [self.window makeKeyAndVisible];
  return YES;
}

@end

</code></pre>
<p>Finally add <code>HAVE_GOOGLE_MAPS=1</code> to Preprocessor Macros both for debug and release. That is found under Build Settings &gt; Preprocessor Macros. Make sure All selected not Basic.</p><figure class="kg-card kg-image-card"><img src="https://cuneyt.aliustaoglu.biz/en/content/images/2019/03/Screen-Shot-2019-03-05-at-00.20.59.png" class="kg-image" alt="Using Google Maps as Provider in IOS with React Native"></figure><p>If you miss this step you'll get below error:</p><figure class="kg-card kg-image-card"><img src="https://cuneyt.aliustaoglu.biz/en/content/images/2019/03/Screen-Shot-2019-03-05-at-00.22.17.png" class="kg-image" alt="Using Google Maps as Provider in IOS with React Native"><figcaption>Use of undeclared identifier 'AIRGoogleMapOverlay'</figcaption></figure><p>After this we're ready to change Apple maps into Google maps just by changing <code>provider="google"</code>. If you remove this tag it'll switch back to Apple Maps.</p><figure class="kg-card kg-image-card"><img src="https://cuneyt.aliustaoglu.biz/en/content/images/2019/03/Screen-Shot-2019-03-05-at-00.24.10.png" class="kg-image" alt="Using Google Maps as Provider in IOS with React Native"><figcaption>Google maps for react-native-maps in IOS</figcaption></figure><figure class="kg-card kg-image-card"><img src="https://cuneyt.aliustaoglu.biz/en/content/images/2019/03/Screen-Shot-2019-03-05-at-00.24.36.png" class="kg-image" alt="Using Google Maps as Provider in IOS with React Native"><figcaption>Apple Maps</figcaption></figure>]]></content:encoded></item><item><title><![CDATA[Setting up and debugging a React Native application]]></title><description><![CDATA[Debugging in React Native is not straightforward. But it's not hard to set up. Here's a complete guide on how to set up a React Native application and enable debugging. ]]></description><link>https://cuneyt.aliustaoglu.biz/en/setting-up-and-debugging-a-react-native-application/</link><guid isPermaLink="false">5c47307b250093415b9205e5</guid><category><![CDATA[React Native]]></category><category><![CDATA[Debugging]]></category><dc:creator><![CDATA[Cuneyt Aliustaoglu]]></dc:creator><pubDate>Wed, 30 Jan 2019 10:21:41 GMT</pubDate><media:content url="https://cuneyt.aliustaoglu.biz/en/content/images/2019/01/banner-2.png" medium="image"/><content:encoded><![CDATA[<img src="https://cuneyt.aliustaoglu.biz/en/content/images/2019/01/banner-2.png" alt="Setting up and debugging a React Native application"><p>Debugging in React Native is not straightforward. But it's not hard to set up. Here's a complete guide on how to set up a React Native application and enable debugging. </p><p>Install react native cli globally.</p><pre><code class="language-bash">npm install -g react-native-cli
</code></pre>
<p>Note that we install "react-native-cli" but use the command "react-native". Don't install react-native globally. </p><p>I will use Genymotion as the emulator. There are other alternatives or you can use your Android device directly but it's faster to use an emulator. Download and install it from here:</p><p><a href="https://www.genymotion.com">https://www.genymotion.com</a></p><p>You need to create a personal account and login. Genymotion is free for personal projects but you need to purchase a subscription for commercial projects. Then add a virtual device using the plus button. I've added Google Pixel 2 on Android 8.0. But it does not matter much. And start the instance.</p><figure class="kg-card kg-image-card"><img src="https://cuneyt.aliustaoglu.biz/en/content/images/2019/01/image-1.png" class="kg-image" alt="Setting up and debugging a React Native application"></figure><p>If you don't want to hassle with GenyMotion, you can always use the free, official emulator from Android Studio.</p><p>Now create a new project using:</p><pre><code class="language-bash">react-native init my-rn-project
</code></pre>
<p>Add below scripts to package.json</p><pre><code class="language-json">{
  &quot;bundle-android&quot;: &quot;react-native bundle --platform android --dev false --entry-file index.js --bundle-output android/app/src/main/assets/index.android.bundle --assets-dest android/app/src/main/res &amp;&amp; react-native run-android&quot;,
  &quot;bundle-ios&quot;: &quot;react-native bundle --dev false --entry-file index.js --bundle-output ios/main.jsbundle --platform ios&quot;,
  &quot;postinstall&quot;: &quot;rndebugger-open --port 8081&quot;
}
</code></pre>
<p>If you run any of those scripts, it will fail. Because by default '/assets' folder under 'android/app/src/main' does not exist. Create this empty folder so when you run the script it will fail creating the file "index.android.bundle" under this folder.</p><figure class="kg-card kg-image-card"><img src="https://cuneyt.aliustaoglu.biz/en/content/images/2019/01/image.png" class="kg-image" alt="Setting up and debugging a React Native application"></figure><p>Now run:</p><pre><code class="language-bash">npm run bundle-android
</code></pre>
<p>If this fails, probably your ANDROID_HOME variable is not set. Find your Android SDK location and save it in environment variables. For me it was like below:</p><pre><code>export ANDROID_HOME=/Users/cuneytaliustaoglu/Library/Android/sdk
</code></pre>
<p>If still fails with error code INSTALL_FAILED_VERIFICATION_FAILURE, run below that allows adb to install apps from unknown resources.</p><pre><code class="language-bash">adb shell settings put global verifier_verify_adb_installs 0
adb shell settings put global package_verifier_enable 0
</code></pre>
<p>Now run below commands. But before running this make sure that GenyMotion is running a virtual Android image. Make sure npm install is run.</p><pre><code class="language-bash">npm install
adb reverse tcp:8081 tcp:8081
react-native run-android
react-native start
</code></pre>
<p>Now we should be able to see "Welcome to React Native!" message. </p><figure class="kg-card kg-image-card"><img src="https://cuneyt.aliustaoglu.biz/en/content/images/2019/01/image-2.png" class="kg-image" alt="Setting up and debugging a React Native application"></figure><p>But we are not finished. We need to be able to debug this application.</p><p>Install react-native-debugger standalone application from below:</p><p><a href="https://github.com/jhen0409/react-native-debugger">https://github.com/jhen0409/react-native-debugger</a></p><p>For Mac ```brew update &amp;&amp; brew cask install react-native-debugger``` will install it using Brew. Or you can install it using the binaries.</p><p>Also install the npm package for this and save it as dev dependencies. This is needed to open the standalone react-native debugger from the emulator when we click "Remote JS Debugging".</p><p><a href="https://github.com/jhen0409/react-native-debugger/tree/master/npm-package">https://github.com/jhen0409/react-native-debugger/tree/master/npm-package</a></p><pre><code class="language-bash">npm i --save-dev react-native-debugger-open
</code></pre>
<p>Now add another script to package.json</p><p>and run ```npm run postinstall```</p><p>Now on your Android Emulator shake or press menu button for dev menu and click "Debug JS Remotely". It will open React Native Debugger and you'll be able to put breakpoints. Below see the familiar debugger which comes with Redux Devtools. </p><figure class="kg-card kg-image-card"><img src="https://cuneyt.aliustaoglu.biz/en/content/images/2019/01/image-5.png" class="kg-image" alt="Setting up and debugging a React Native application"></figure><p>Access to your non transpiled code (ES6 code) under RNDebuggerWorker.js and put breakpoints. You can also enable hot reloading from the same menu.</p>]]></content:encoded></item><item><title><![CDATA[Running GUI applications in Docker on Windows, Linux and Mac hosts]]></title><description><![CDATA[There's WineHQ to run Windows applications on Linux (and macOS). But, what about other way around? What if you want to run Linux applications on Windows (and Mac). Docker is the answer but it if you think that Docker only can run terminal applications then you're wrong.]]></description><link>https://cuneyt.aliustaoglu.biz/en/running-gui-applications-in-docker-on-windows-linux-mac-hosts/</link><guid isPermaLink="false">5bf524fc250093415b9205c8</guid><category><![CDATA[Docker]]></category><dc:creator><![CDATA[Cuneyt Aliustaoglu]]></dc:creator><pubDate>Sat, 24 Nov 2018 10:16:54 GMT</pubDate><media:content url="https://cuneyt.aliustaoglu.biz/en/content/images/2018/11/DockerX11.png" medium="image"/><content:encoded><![CDATA[<img src="https://cuneyt.aliustaoglu.biz/en/content/images/2018/11/DockerX11.png" alt="Running GUI applications in Docker on Windows, Linux and Mac hosts"><p>There's WineHQ to run Windows applications on Linux (and macOS). But, what about other way around? What if you want to run Linux applications on Windows (and Mac). Docker is the answer but it if you think that Docker only can run terminal applications then you're wrong.</p><p>Q: Why would you like to run GUI applications inside Docker?</p><p>A: Bad question. I don't know. This tutorial is not about the reason. It could be any reason.</p><p>Q: How can you run GUI applications inside a Docker container on Windows, Linux and Mac hosts?</p><p>A: Good question. I know the answer.</p><p>I am using a simple Docker image that I have created and uploaded into DockerHub. You can use it directly or make your own. It's as simple as this:</p><pre><code>FROM ubuntu:14.04

RUN apt-get update
RUN apt-get -y install firefox

CMD [&quot;firefox&quot;]
</code></pre>
<h3 id="forwindows">For Windows</h3>
<ul><li>Install  <strong>VcXsrv Windows X Server </strong>using the address below</li></ul><p>	<a href="https://sourceforge.net/projects/vcxsrv/">https://sourceforge.net/projects/vcxsrv/</a></p><ul><li>Install &amp; start XLaunch with usual Windows setup (a.k.a. next next) until you get to Extra Settings. Check all options as below and finish configuration. It's important to disable access control. Otherwise, the request from Docker will be rejected.</li></ul><figure class="kg-card kg-gallery-card kg-width-wide"><div class="kg-gallery-container"><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://cuneyt.aliustaoglu.biz/en/content/images/2018/11/xlaunch.png" width="499" height="392" alt="Running GUI applications in Docker on Windows, Linux and Mac hosts"></div></div></div><figcaption>XLaunch settings</figcaption></figure><ul><li>Get your IP address using ipconfig command (My IP address was 192.168.1.68 yours might be different)</li><li>Run Firefox GUI as below</li></ul><blockquote>docker run --rm -it -e DISPLAY=192.168.1.68:0.0 aliustaoglu/firefox</blockquote><p>This will create a container and from this container Firefox will run. When you finish with it, the container will be removed (--rm)</p><figure class="kg-card kg-gallery-card kg-width-wide"><div class="kg-gallery-container"><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://cuneyt.aliustaoglu.biz/en/content/images/2018/11/JessieFirefox.jpg" width="1444" height="900" alt="Running GUI applications in Docker on Windows, Linux and Mac hosts"></div></div></div><figcaption>Firefox running inside Docker on Windows HOST</figcaption></figure><p>Quite easy</p><h3 id="formac">For Mac</h3>
<p>For macOS we need to install xQuartz. You can use brew:</p><blockquote>brew cask install xQuartz</blockquote><p>Or download the dmg file:</p><p><a href="https://www.xquartz.org/">https://www.xquartz.org/</a></p><p>After installing xQuartz, run it and check the option "Allow connections from network clients". Keep xQuarts running.</p><figure class="kg-card kg-image-card"><img src="https://cuneyt.aliustaoglu.biz/en/content/images/2018/11/image-2.png" class="kg-image" alt="Running GUI applications in Docker on Windows, Linux and Mac hosts"></figure><p>Now find your local IP address using <code>ifconfig</code> or any other method you know. My address was 192.168.1.76. And run bellow command:</p><blockquote>xhost + 192.168.1.76</blockquote><p>Now we are ready to run the docker image:</p><blockquote>docker run --rm -e DISPLAY=192.168.1.76:0 -v /tmp/.X11-unix:/tmp/.X11-unix aliustaoglu/firefox</blockquote><h3 id="forlinux">For Linux</h3>
<p>X11 (X Windows System) is the GUI environment in Unix operating systems. Since it's a native Linux platform we don't need to install xQuartz or XLaunch as Linux already has it. We only need to run this command:</p><blockquote>docker run --rm -e DISPLAY -v /tmp/.X11-unix:/tmp/.X11-unix aliustaoglu/firefox</blockquote>]]></content:encoded></item><item><title><![CDATA[How to generate harmonica tabs from MIDI]]></title><description><![CDATA[MIDI files are great if you play harmonica. They have all the notes you need whether you have chromatic or diatonic harmonica in whichever key. If you have the right software any MIDI file can become a tab source.]]></description><link>https://cuneyt.aliustaoglu.biz/en/how-to-generate-harmonica-tabs-from-midi/</link><guid isPermaLink="false">5bf25969250093415b9205c1</guid><category><![CDATA[Harmonica]]></category><dc:creator><![CDATA[Cuneyt Aliustaoglu]]></dc:creator><pubDate>Mon, 19 Nov 2018 07:36:42 GMT</pubDate><media:content url="https://cuneyt.aliustaoglu.biz/en/content/images/2018/11/HeyJude-1.png" medium="image"/><content:encoded><![CDATA[<img src="https://cuneyt.aliustaoglu.biz/en/content/images/2018/11/HeyJude-1.png" alt="How to generate harmonica tabs from MIDI"><p>MIDI files are great if you play harmonica. They have all the notes you need whether you have chromatic or diatonic harmonica in whichever key. If you have the right software any MIDI file can become a tab source.</p><p>Luckily there's this awesome software called MuseScore and its absolutely free. It may look like it's for note readers not tab but you'll be very wrong if you think so.</p><p><a href="https://musescore.org/">https://musescore.org/</a></p><p>Download and install it if you haven't done so.</p><p>And let's find a popular and easy song to transcribe. "Hey Jude" from Beatles is one of the most popular songs on harmonica tabs websites. Just google and find one like below:</p><p><a href="https://earlybeatles.com/heyjude.html">https://earlybeatles.com/heyjude.html</a></p><p>MuseScore can open MIDI files. Just open this and you'll see something like below:</p><figure class="kg-card kg-image-card"><img src="https://cuneyt.aliustaoglu.biz/en/content/images/2018/11/image.png" class="kg-image" alt="How to generate harmonica tabs from MIDI"><figcaption>Hey Jude by Beatles in MuseScore</figcaption></figure><p>But those are music notes not harmonica. For harmonica we need to download a plugin that converts notes into harp tabs. Navigate to below page and download <code>harmonica_tablature.qml</code> file. (Or you can download whole repository as a zip file and get the file we need)</p><p><a href="https://github.com/lasconic/harmonica_tablature">https://github.com/lasconic/harmonica_tablature</a></p><p>Now go to Preferences and find where the Plugins folder is located:</p><figure class="kg-card kg-image-card"><img src="https://cuneyt.aliustaoglu.biz/en/content/images/2018/11/MuseScore-Preferences.png" class="kg-image" alt="How to generate harmonica tabs from MIDI"><figcaption>MuseScore Preferences</figcaption></figure><p>Find this folder and place harmonica_tablature.qml inside it. Now from the menu go to<code>Plugins &gt; Plugin Manager</code> , select harmonica_tablature and click OK.</p><figure class="kg-card kg-image-card"><img src="https://cuneyt.aliustaoglu.biz/en/content/images/2018/11/harmonica_tablature-plugin.png" class="kg-image" alt="How to generate harmonica tabs from MIDI"><figcaption>Plugin Manager</figcaption></figure><p>Now you will notice that plugin installed if you click Plugins menu again:</p><figure class="kg-card kg-image-card"><img src="https://cuneyt.aliustaoglu.biz/en/content/images/2018/11/image-1.png" class="kg-image" alt="How to generate harmonica tabs from MIDI"></figure><p>MuseScore, by default, shows all tracks. We don't want to see guitar, piano etc. tracks. We want to get only the vocal track for this song.</p><figure class="kg-card kg-image-card"><img src="https://cuneyt.aliustaoglu.biz/en/content/images/2018/11/tracks.png" class="kg-image" alt="How to generate harmonica tabs from MIDI"><figcaption>Select vocals only and click Apply</figcaption></figure><p>Now click the Harmonica Tablature and see that it works. Feel free to add titles and export as pdf. Also if the songs key does not match your harmonica, you can transpose it into the whatever key you have be it diatonic or chromatic.</p><figure class="kg-card kg-image-card"><img src="https://cuneyt.aliustaoglu.biz/en/content/images/2018/11/HeyJude.png" class="kg-image" alt="How to generate harmonica tabs from MIDI"></figure><p>Now google your favourite song and find a MIDI file for it.</p>]]></content:encoded></item><item><title><![CDATA[Using Docker and Claudia.js to deploy Lambda functions to AWS]]></title><description><![CDATA[I am using Claudia.js (http://claudia.js) to deploy my Lambda functions to AWS and link them to my API Gateway or S3 bucket. Real easy solution. No extra YML or learning process. Just an npm package installed globally and hooked into your package.json scripts.]]></description><link>https://cuneyt.aliustaoglu.biz/en/using-docker-and-claudia-js-to-deploy-lambda-functions/</link><guid isPermaLink="false">5bf0bb7d250093415b9205ba</guid><category><![CDATA[Docker]]></category><category><![CDATA[AWS]]></category><dc:creator><![CDATA[Cuneyt Aliustaoglu]]></dc:creator><pubDate>Sun, 18 Nov 2018 03:50:56 GMT</pubDate><media:content url="https://cuneyt.aliustaoglu.biz/en/content/images/2018/11/DockerClaudia-1.png" medium="image"/><content:encoded><![CDATA[<img src="https://cuneyt.aliustaoglu.biz/en/content/images/2018/11/DockerClaudia-1.png" alt="Using Docker and Claudia.js to deploy Lambda functions to AWS"><p>I am using Claudia.js (http://claudia.js) to deploy my Lambda functions to AWS and link them to my API Gateway or S3 bucket. Real easy solution. No extra YML or learning process. Just an npm package installed globally and hooked into your package.json scripts. But if you use your local machine to deploy Lambda, you might run into some problems with the artifacts not matching AWS environment. It may not be a problem if all your dependencies are pure JavaScript; but, if you have any dependencies that use low level C/C++ add ons (eg. <a href="https://github.com/lovell/sharp">https://github.com/lovell/sharp</a>) you will face problems.</p><p>Below simple lambda function will not run (assuming your local machine is not compatible with Amazon) if you add sharp library to your dependencies and upload the zip (or deploy using Claudia)</p><blockquote>npm install --save sharp</blockquote><pre><code class="language-js">const sharp = require('sharp')

exports.handler = async event =&gt; {
    console.log('Hello')
};

</code></pre>
<p>You will get below error:</p><pre><code>module initialization error: Error
at Object.hasVendoredLibvips (/var/task/node_modules/sharp/lib/libvips.js:61:13)
at Object.&lt;anonymous&gt; (/var/task/node_modules/sharp/lib/constructor.js:9:22)
at Module._compile (module.js:652:30)
at Object.Module._extensions..js (module.js:663:10)
at Module.load (module.js:565:32)
at tryModuleLoad (module.js:505:12)
at Function.Module._load (module.js:497:3)
at Module.require (module.js:596:17)
at require (internal/module.js:11:18)
at Object.&lt;anonymous&gt; (/var/task/node_modules/sharp/lib/index.js:3:15)</code></pre><p>It's also always good to compile/transpile the code with the same Node version as AWS Lambda has. Using EC2 or virtual machine would be the most perfect solution if it wasn't impractical (and it will cost too). We will use Docker. At the moment AWS is using Node 8.10. So, we will use an image that has this version. This image mimics the live AWS Lambda environment.</p><p><a href="https://hub.docker.com/r/lambci/lambda/">https://hub.docker.com/r/lambci/lambda/</a></p><p>Since Claudia.js will work inside the Docker, we will need to pass AWS credentials to Docker. In my local machine credentials are stored in  <code>~/.aws/credential</code> , but in the image it's under /root/.aws/credential. So, when I will create a volume that will do this mapping. Also, I need to copy all my working documents (including package.json and all my code) into Docker. I can also create volume for this. I will map my working folder ($PWD) into /claudia folder inside the container. </p><p>Create a bash script (eg. claudia-deploy.sh) and update it's content like below:</p><pre><code class="language-bash">docker run -v $PWD:/claudia -v $HOME/.aws:/root/.aws --rm lambci/lambda:build-nodejs8.10 /bin/bash -c &quot;\
cd /claudia
rm -rf node_modules
npm install -g claudia
npm run claudia-deploy
&quot;
</code></pre>
<p>And make this script runnable</p><blockquote>chmod +x claudia-deploy.js</blockquote><p>Add below scripts inside package.json</p><pre><code class="language-json">{
&quot;scripts&quot;: {
    &quot;claudia-deploy&quot;: &quot;claudia create --no-optional-dependencies --name my-aws-function --handler index.handler&quot;,
    &quot;claudia-update&quot;: &quot;claudia update --no-optional-dependencies&quot;,
    &quot;deploy&quot;: &quot;./claudia-deploy.sh&quot;,
    &quot;update&quot;: &quot;./claudia-update.sh&quot;,
  }
}
</code></pre>
<p>You should not run <code>npm run claudia-deploy</code> or <code>npm run claudia-update</code> in our local machine. It needs to be run inside the container. We need to execute <code>npm run claudia-deploy</code> inside our local machine and it will run the claudia scripts inside the container.</p><blockquote>npm run build</blockquote><p>For update, we need another bash script (claudia-update.sh)</p><pre><code class="language-bash">docker run -v $PWD:/claudia -v $HOME/.aws:/root/.aws --rm lambci/lambda:build-nodejs8.10 /bin/bash -c &quot;\
cd /claudia
rm -rf node_modules
npm install -g claudia
npm run claudia-update
&quot;
</code></pre>
<p>Then run</p><blockquote>npm run update</blockquote><p>Using this method we make sure that all the artifacts that we generate are fully compatible with AWS. Like I said before, if you're not using any dependencies or your dependencies are simple JS libraries you'll most likely be OK. But, if you use libraries like sharp (to reduce the size of the images uploaded into S3 bucket), you need a solution like this.</p>]]></content:encoded></item></channel></rss>