<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Anton Korepanov</title>
    <description>The latest articles on DEV Community by Anton Korepanov (@ampersd).</description>
    <link>https://dev.to/ampersd</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F914452%2F1c9deb12-e613-4bd9-a33f-c2818fe6390d.jpeg</url>
      <title>DEV Community: Anton Korepanov</title>
      <link>https://dev.to/ampersd</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ampersd"/>
    <language>en</language>
    <item>
      <title>Expo SDK and native module</title>
      <dc:creator>Anton Korepanov</dc:creator>
      <pubDate>Fri, 16 Sep 2022 18:20:11 +0000</pubDate>
      <link>https://dev.to/ampersd/expo-sdk-and-native-module-4f7m</link>
      <guid>https://dev.to/ampersd/expo-sdk-and-native-module-4f7m</guid>
      <description>&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffj54ijiu7s49w1wo48yg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffj54ijiu7s49w1wo48yg.png" alt="scooter"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Disclaimer:&lt;br&gt;
If you find any inaccuracy in the text below, feel free to promote your finding so I can fix it&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Hi, my name is Anton. I'm a full-stack software engineer. At the moment working on the React Native app for the scooter building company.&lt;/p&gt;

&lt;p&gt;In this post, I will share my experience of adding additional native code functionality inside my app. I spent around a month trying to figure out different things around the process. This is what I learned:&lt;/p&gt;
&lt;h2&gt;
  
  
  Expo SDK and my setup
&lt;/h2&gt;

&lt;p&gt;I use Expo SDK 45. When I write down these lines, Expo SDK 46 has already been released, but I haven't updated it yet.&lt;/p&gt;

&lt;p&gt;What is Expo? It's a managed platform, which simplifies the process of development react native app. &lt;/p&gt;

&lt;p&gt;Speaking about the ways to develop the app in react native ecosystem, we have two possibilities:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;bare workflow&lt;/strong&gt;. Usual react native project with two folders &lt;code&gt;android&lt;/code&gt; and &lt;code&gt;ios&lt;/code&gt; with the native platform configuration there (like &lt;code&gt;AndroidManifest.xml&lt;/code&gt; and &lt;code&gt;Info.plist&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;managed workflow&lt;/strong&gt;. Expo manages these &lt;code&gt;android&lt;/code&gt; and &lt;code&gt;ios&lt;/code&gt; folders for us. It's quite convenient.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Everything looks bright when you live inside the Expo ecosystem and don't need some tricky native platforms API functions, which aren't officially supported by the Expo.&lt;/p&gt;

&lt;p&gt;BUT if you are faced with the need to use some native unsupported stuff - don't despair, may you be comforted by the thought that you are not alone. &lt;/p&gt;

&lt;p&gt;Here is my story...&lt;/p&gt;
&lt;h2&gt;
  
  
  Business task
&lt;/h2&gt;

&lt;p&gt;Let me start from the domain field and business requirements:&lt;/p&gt;

&lt;p&gt;&lt;del&gt;Make the world a better place&lt;/del&gt; &lt;br&gt;
&lt;strong&gt;Make the phone work as a BLE key for the scooter&lt;/strong&gt; (BLE = Bluetooth Low Energy = Bluetooth 4.0).&lt;/p&gt;

&lt;p&gt;BLE allows the device to work in two modes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;central mode&lt;/strong&gt; (like the client, which fetches information from other devices)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;peripheral mode&lt;/strong&gt; (like the server, which provides some information endpoints)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I needed the phone to work in peripheral mode and advertise it with a custom name like "My supercool iPhone" (to tell the nearby devices that it exists).&lt;/p&gt;

&lt;p&gt;I'd searched through google and GitHub for possible 3rd-party modules to work with this, but found nothing.&lt;/p&gt;

&lt;p&gt;The most stable native module &lt;a href="https://github.com/dotintent/react-native-ble-plx" rel="noopener noreferrer"&gt;react-native-ble-plx&lt;/a&gt; allows working only in central mode.&lt;/p&gt;

&lt;p&gt;Other solutions were either unstable, without the support of one of the platforms (Android/iOS), or without the feature to set a custom name when you advertise.&lt;/p&gt;

&lt;p&gt;Long story short, I ended up with the need to write my native module to wrap up Kotlin and Swift code for work with this Bluetooth stuff.&lt;/p&gt;
&lt;h2&gt;
  
  
  Native module development
&lt;/h2&gt;

&lt;p&gt;I wouldn't spend much time talking about the native module development itself, but here are some useful links:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://reactnative.dev/docs/native-modules-intro" rel="noopener noreferrer"&gt;official docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/callstack/react-native-builder-bob" rel="noopener noreferrer"&gt;project structure generator&lt;/a&gt; which I used, during the initial configuration, allows you to choose languages for the native platforms (like Kotlin and Swift).&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Platform and Code
&lt;/h2&gt;

&lt;p&gt;When working with Expo or React Native we can split the whole system on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Platform&lt;/strong&gt; - underneath layer with all required integrations to the native platforms + JS engine + Bridge&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Code&lt;/strong&gt; - our js code, which we usually write&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When we add some additional native functions to our Platform we need to create our own client instead of &lt;code&gt;Expo Go&lt;/code&gt; we normally use. This client with all additional features inside is called &lt;code&gt;Dev Client&lt;/code&gt; (in Expo terminology). &lt;/p&gt;

&lt;p&gt;We need to build dev clients for both Android and iOS.&lt;/p&gt;

&lt;p&gt;Dev clients are built using &lt;code&gt;EAS&lt;/code&gt; tool. They can be built locally or remotely (on expo team cloud infrastructure). The problem with the last is that you have to wait in a queue on the free tier (up to 3 hours).&lt;/p&gt;

&lt;p&gt;To run builds locally, I use the following commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"start"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"expo start"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"start-dev"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"expo start --dev-client"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"build-dev-client-android"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"eas build -p android --profile=development --local"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"build-dev-client-ios"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"eas build -p ios --profile=development --local"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"release-android"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"eas build -p android --profile release --local"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"release-ios"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"eas build --platform ios --profile release --local"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;in case you may need it, my &lt;code&gt;eas.json&lt;/code&gt; is here:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"build"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"base"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"env"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"release"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"android"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"development"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"extends"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"base"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"developmentClient"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"distribution"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"internal"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"releaseChannel"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"default"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"android"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"buildType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"apk"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"submit"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"release"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"cli"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&amp;gt;= 0.44.1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"requireCommit"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;if you want local builds to be saved to some other folder (by default, they are saved in the project folder) - create &lt;a href="https://docs.expo.dev/build-reference/local-builds/#using-local-builds-for-debugging" rel="noopener noreferrer"&gt;EAS_LOCAL_BUILD_ARTIFACTS_DIR&lt;/a&gt; environment variable with the desired path.&lt;/p&gt;

&lt;p&gt;On my old machine (MacBook Pro late 2015), a local build can take around 20-25 minutes for one package. (yet it's faster than waiting 3 hours in a cloud build queue :))&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NOTE&lt;/strong&gt;. In defense of my old MacBook buddy, I should say that on Expo servers the build of my react native project takes a similar amount of time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Platform configuration (dev-client)
&lt;/h2&gt;

&lt;p&gt;During the process of dev-client creating, when we use our ready native module and add it as a dependency to our expo app, it should automatically link all native stuff using react native auto-linking feature.&lt;/p&gt;

&lt;p&gt;The only problem we may encounter here is related to permissions. In my case, I need additional Bluetooth permissions to set up.&lt;/p&gt;

&lt;p&gt;Here we need to intercept in the expo process of building the apps/dev-clients (how it builds &lt;code&gt;android&lt;/code&gt; and &lt;code&gt;ios&lt;/code&gt; projects). We doing it using &lt;a href="https://docs.expo.dev/guides/config-plugins/" rel="noopener noreferrer"&gt;config-plugins&lt;/a&gt;. We can add some required modifications to the &lt;code&gt;AndroidManifest.xml&lt;/code&gt; and &lt;code&gt;Info.plist&lt;/code&gt; on this step.&lt;/p&gt;

&lt;p&gt;I was lucky because there already was one written config that added Bluetooth permissions to native projects &lt;a href="https://github.com/expo/config-plugins/tree/main/packages/react-native-ble-plx" rel="noopener noreferrer"&gt;config-plugins/react-native-ble-plx&lt;/a&gt;. I simply used it.&lt;/p&gt;

&lt;p&gt;We can test if expo modifies native folders as we suppose it should by running the command &lt;code&gt;expo prebuild&lt;/code&gt; and discovering the results (be ready to discard these changes from the repo if you use git and don't want to commit them).&lt;/p&gt;

&lt;p&gt;&lt;code&gt;expo prebuild&lt;/code&gt; is the command which actually runs first by &lt;code&gt;EAS&lt;/code&gt; (expo build tool) when you try to build *.apk/*.aab or *.ipa packages. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NOTE&lt;/strong&gt;. When your config plugin is ready don't forget to add it to "plugins" section of &lt;code&gt;app.json&lt;/code&gt; file. &lt;/p&gt;

&lt;h1&gt;
  
  
  Dev-client artifacts
&lt;/h1&gt;

&lt;p&gt;So, at this point, you should have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;dev-client.apk&lt;/code&gt; or Android. Can be installed on the real device by running &lt;code&gt;adb install dev-client.apk&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;dev-client.ipa&lt;/code&gt; for iOS. I found a way to install it on the real iPhone 11 by downloading &lt;code&gt;Apple Configurator&lt;/code&gt; app from Mac App Store. There you can simply drag and drop the package on your phone and it will be installed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When you have packages installed on your real device, you can run your JS code server by &lt;code&gt;expo start --dev-client&lt;/code&gt; (don't mess it up  with the &lt;code&gt;expo start&lt;/code&gt; command, without the flag it will start the &lt;code&gt;Expo Go&lt;/code&gt; app instead of our dev client)&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;If you want to write your native module for any native feature and use it with Expo, you have to follow these steps:&lt;/p&gt;

&lt;p&gt;1) use &lt;code&gt;react-native-builder-bob&lt;/code&gt; to generate a native library and test it with the bare react native app ('bare' means 'no managed workflow', just pure react native with iOS and android folders in the project)&lt;br&gt;
2) if you want to work inside the Expo environment further, you need to build a 'platform' for your react native JS code, which will include your native module besides the basic expo supporting functions. This 'platform' is called 'dev-client' in terms of the expo.&lt;br&gt;
3) if your native module needs some permissions, you need to write your own expo-config to modify the platforms permission files, you can take some existing ones as a basis&lt;br&gt;
4) Then you need to install this platform/dev-client on your real device and start expo with the special &lt;code&gt;--dev-client&lt;/code&gt; flag. &lt;br&gt;
5) In this way, you may continue to get all the benefits that Expo offers. &lt;/p&gt;

&lt;p&gt;I admit that it can be tricky when you go through this technology and dependencies jungle for the first time. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NOTE&lt;/strong&gt;. Recently, I found out that the Expo ecosystem has its way to work with native modules called &lt;a href="https://docs.expo.dev/modules/module-api/" rel="noopener noreferrer"&gt;Module API&lt;/a&gt;. I haven't tested this workflow yet. Probably, it will simplify some steps. I think this could serve as material for another article ;)&lt;/p&gt;

&lt;p&gt;That's all for now. Thanks for your time. I hope you enjoyed the reading.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9k9ov9efeuyic2f3pfqf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9k9ov9efeuyic2f3pfqf.png" alt="scooter with the girl"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>reactnative</category>
      <category>expo</category>
      <category>javascript</category>
      <category>mobile</category>
    </item>
  </channel>
</rss>
