<?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>Forem: Kevin Schildhorn</title>
    <description>The latest articles on Forem by Kevin Schildhorn (@kevinschildhorn).</description>
    <link>https://forem.com/kevinschildhorn</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%2F289741%2F9ddd5456-0995-46f4-be06-e236129a131a.jpg</url>
      <title>Forem: Kevin Schildhorn</title>
      <link>https://forem.com/kevinschildhorn</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/kevinschildhorn"/>
    <language>en</language>
    <item>
      <title>Under the hood of Relay, Android Studio plugin for exporting Figma components to compose</title>
      <dc:creator>Kevin Schildhorn</dc:creator>
      <pubDate>Wed, 16 Nov 2022 20:18:53 +0000</pubDate>
      <link>https://forem.com/kevinschildhorn/under-the-hood-of-relay-android-studio-plugin-for-exporting-figma-components-to-compose-41p6</link>
      <guid>https://forem.com/kevinschildhorn/under-the-hood-of-relay-android-studio-plugin-for-exporting-figma-components-to-compose-41p6</guid>
      <description>&lt;p&gt;Recently Google announced &lt;a href="https://www.youtube.com/watch?v=6PgzGXZt5Fg" rel="noopener noreferrer"&gt;Relay&lt;/a&gt;, a new process that allows teams to create UI in Figma and generate high-fidelity Compose UI components. It's currently in open alpha, and can be installed as a plugin for Android Studio. It's a really interesting tool that automatically generates compose code based on the components you choose to export from Figma. There's a overview of what it does and how to run it &lt;a href="https://developer.android.com/jetpack/compose/tooling/relay" rel="noopener noreferrer"&gt;here&lt;/a&gt;, but you may be asking: what's really going on under the hood? So let's go over in detail the process from Figma to Compose code using Relay.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: this is not a tutorial for Figma, rather just a look at what code is generated and how it compares to custom compose code.&lt;/p&gt;

&lt;p&gt;Note: Relay is a new and constantly evolving library, so some of these findings may change in the future. For reference I am testing Relay version "0.3.00".&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Step 1. Figma
&lt;/h2&gt;

&lt;p&gt;For this blog I made an example Figma page with a simple component, a button.&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%2Fdl43dg648ocy7isllps4.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%2Fdl43dg648ocy7isllps4.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This button is somewhat simple, it's a rectangular shape with a text as a subview. It has been exported as a component, and has some properties defined in its design.&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%2Ft7ix70mjlvqk36jmn73r.jpeg" 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%2Ft7ix70mjlvqk36jmn73r.jpeg" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you're not familiar with Figma, that's fine. Here are the main things to point out:&lt;/p&gt;

&lt;p&gt;The button:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;has a radius of 15&lt;/li&gt;
&lt;li&gt;Has external padding of 10&lt;/li&gt;
&lt;li&gt;Is centrally aligned&lt;/li&gt;
&lt;li&gt;Has a Color of &lt;em&gt;Primary&lt;/em&gt;*&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Most of these are straight forward properties, one to note is the Color, which is defined in Figma as a Color Style so it can be re-used.&lt;/p&gt;

&lt;p&gt;The Text:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fills the button&lt;/li&gt;
&lt;li&gt;Has a Typography of &lt;em&gt;Button&lt;/em&gt;*&lt;/li&gt;
&lt;li&gt;Has a defined content(text)&lt;/li&gt;
&lt;li&gt;Has Text Centered both horizontally and vertically&lt;/li&gt;
&lt;li&gt;Has a Color of PrimaryText&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Again most things are straight forward but it's worth noting the Text Style &lt;em&gt;Button&lt;/em&gt; is defined in Figma. It contains the font name, style, and size (as well as some spacing).&lt;/p&gt;

&lt;p&gt;Looks pretty good (I hope!), let's export it and see what we get in Android Studio.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2. UI Packages
&lt;/h2&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%2F1xag3oms9aw0zpioum51.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%2F1xag3oms9aw0zpioum51.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In Android Studio we've imported this button as a ui-package. Here we can see some small files like text files and a config file. We also see a &lt;code&gt;font&lt;/code&gt; folder, containing all the variants of the font used in the button, which is interesting. &lt;strong&gt;By default Relay puts all the fonts into each ui-package, even if they are shared between components.&lt;/strong&gt; In other words if you have two components(like a button and a textfield) and they both use the same custom font, then Relay will add two copies of that same font to your project rather than referencing one copy. There is an experimental feature to use shared typography but that's beyond the scope of this post.&lt;/p&gt;

&lt;p&gt;Let's look at the json file. Here we can see everything we've defined. Each part of the component has its own name and id, and is defined in "atoms" (This seems to be in align with the concept of &lt;a href="https://atomicdesign.bradfrost.com/chapter-2/" rel="noopener noreferrer"&gt;Atomic Design&lt;/a&gt;). Then in "modes" we can see all the properties from Figma.&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;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"button_primary"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"source-key"&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="err"&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="err"&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;"design"&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;"atoms"&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;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"group"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"top_level"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"root"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&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;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"buttonText"&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;"modes"&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;"Button/Primary"&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;"rules"&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;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"top_level"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="err"&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;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"buttonText"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="err"&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="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="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;It's a large file so I'll break it up for clarity. We can see that we have our two atoms, and an array of rules associated with the atoms.&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;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"top_level"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"padding"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"10.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"border-radius"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"15.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"main-axis-align"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"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;"cross-axis-align"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"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;"children"&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="s2"&gt;"buttonText"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"item-spacing"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"10.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"background-color"&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;"alpha"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"hue"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"38.82352941176471"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"saturation"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.0"&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;"clip-content"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"false"&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;First off we can see this layer has been renamed to "top_level", which makes sense but can seem odd. It has padding, border-radius, alignment, and the color. &lt;br&gt;
We see that the color "Primary" has been set as an HSV color, with no mention of the color style. Again with the experimental feature we may be able to get this color style, but for now it is a raw color.&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;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"buttonText"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"size-constraints"&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;"width-constraints"&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;"sizing-mode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"proportional"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.0"&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;"height-constraints"&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;"sizing-mode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"proportional"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.0"&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;"font-weight"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"700.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"color"&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;"alpha"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"hue"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"38.4375"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"saturation"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0.25098039215686274"&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;"text-content"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Test Text"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"overflow"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"visible"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"max-lines"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"-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;"text-align-vertical"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"center"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"line-height"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.25"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"typeface"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Quicksand"&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;buttonText gets to keep its name, and here we can see it has sizing constraints, color, text content, alignment and Typeface.&lt;/p&gt;

&lt;p&gt;Here we see that the typeface we nicely set in Figma is gone. Instead we have the name of the font, a font weight rather than a style, and a line height. Strangely the size of the font doesn't appear. It's not the worst, but it does come across a little messy.&lt;/p&gt;

&lt;p&gt;Ok! We've imported the json config, now we can build it and take a look at our generated compose code!&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3. Generated Code
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Composable&lt;/span&gt;
&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;ButtonPrimary&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;modifier&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Modifier&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Modifier&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;TopLevel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;modifier&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;modifier&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;ButtonText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;modifier&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Modifier&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rowWeight&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;1.0f&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;columnWeight&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;1.0f&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here we can see what looks like a straight forward compose function, with the atoms defined from the json. But what are these TopLevel and ButtonText functions? Well, as you can imagine there's a too much generated code to go into for this post, so for brevity I'm going to highlight some key points. If you want to dig deeper into what's generated, I've created a &lt;a href="https://gist.github.com/KevinSchildhorn/c2c66b0d8760ebe0102c305e7413c717" rel="noopener noreferrer"&gt;github gist here&lt;/a&gt; that contains the generated code. Now let's go over some noteworthy code.&lt;/p&gt;

&lt;h3&gt;
  
  
  Relay Wrappers
&lt;/h3&gt;

&lt;p&gt;All of the code generated from Relay has wrapping compose functions with the prefix &lt;em&gt;Relay&lt;/em&gt;. For example the &lt;code&gt;ButtonText&lt;/code&gt; function contains the &lt;code&gt;RelayText&lt;/code&gt; function, which takes in the properties defined in Figma. These wrapper functions are generated in the &lt;code&gt;relay-runtime&lt;/code&gt; package while our particular Button is defined in the &lt;code&gt;relay&lt;/code&gt; package.&lt;/p&gt;

&lt;p&gt;This goes even as far as having a &lt;code&gt;RelayColumn&lt;/code&gt; and &lt;code&gt;RelayRow&lt;/code&gt;, which strangely do not wrap their compose counterparts.&lt;/p&gt;

&lt;h3&gt;
  
  
  Properties
&lt;/h3&gt;

&lt;p&gt;The properties in these wrappers have the same names as mentioned in figma, rather than compose names. For example instead of &lt;code&gt;horizontalArrangement&lt;/code&gt; we have &lt;code&gt;mainAxisAlignment&lt;/code&gt;. Similarly while Relay tries to use as many generic types as possible (i.e. floats, booleans, etc) it doesn't always use Compose types. It does some types like Color, FontWeight and more, but avoids others like &lt;code&gt;Alignment&lt;/code&gt; and &lt;code&gt;TextDecoration&lt;/code&gt;. I'm sure there are technical reasons for this but it really does make it feel like a plugin generated it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conversion
&lt;/h3&gt;

&lt;p&gt;The conversion is interesting, albeit a bit messy. A lot of conversions are simply &lt;code&gt;when&lt;/code&gt; statements, comparing the Figma variables to the compose variables. Most of the conversion is directly in the compose function, rather than having extensions or organizing the code a bit more. Generally speaking for the whole generated code, it's a bit messy and hard to read, but it gets the job done.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bonus: Comparing to Typical code
&lt;/h2&gt;

&lt;p&gt;Now that we've seen what the generated code looks like and how we've gotten there, I decided it would be fun to compare this to how I would personally create this component. Below is my version:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nc"&gt;Button&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;onClick&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/*TODO*/&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="n"&gt;modifier&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Modifier&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fillMaxWidth&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;1.0f&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="n"&gt;shape&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;RoundedCornerShape&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dp&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="n"&gt;elevation&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ButtonDefaults&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;elevation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;defaultElevation&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dp&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="n"&gt;colors&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ButtonDefaults&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;textButtonColors&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;backgroundColor&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;DesignColors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;primary&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nc"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;style&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;typography&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;button&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;color&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;DesignColors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;onPrimary&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;modifier&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Modifier&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dp&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here I'm using the base jetpack components, with the addition of creating text and color styles for re-use.&lt;/p&gt;

&lt;p&gt;It's interesting to see the differences between what a developer might make and what Relay is generating. If you look past the wrappers and name changes both functions are eventually calling the same code. Both use &lt;code&gt;RoundedCornerShape&lt;/code&gt;, &lt;code&gt;padding&lt;/code&gt;, and &lt;code&gt;elevation&lt;/code&gt;, even if you have to dig deeper to see it. The only interesting difference is Relay does not use the composable &lt;code&gt;Button&lt;/code&gt; or &lt;code&gt;Text&lt;/code&gt; functions, but stick with their own custom implementation. That is to be expected when Figma has no concept of android components like buttons. &lt;/p&gt;

&lt;p&gt;So yea, you can see that while different they both use similar code. Of course my code is more concise and easier to read, but does it really matter if no developer is going to manually update the composable? &lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;So that's what's going on under the hood in Relay! There are some things I unfortunately didn't have time to cover such as nested components(spoilers, they work as expected!), adding custom parameters in Figma, mapping styles to compose, and vectors. If these are topics that you'd like me to cover let me know in the comments. Also if you have any questions or would like to see more of the code leave a comment or reach me at my &lt;a href="https://twitter.com/KevinSchildhorn" rel="noopener noreferrer"&gt;twitter&lt;/a&gt;, &lt;a href="https://dev.to/kevinschildhorn"&gt;dev.to&lt;/a&gt;, or on the &lt;a href="https://kotlinlang.slack.com/messages/kotlin-logging/" rel="noopener noreferrer"&gt;Kotlin Slack&lt;/a&gt; (All under &lt;a class="mentioned-user" href="https://dev.to/kevinschildhorn"&gt;@kevinschildhorn&lt;/a&gt;).&lt;/p&gt;

</description>
      <category>android</category>
      <category>figma</category>
      <category>compose</category>
      <category>kotlin</category>
    </item>
    <item>
      <title>Adding tests to your Discord Bot - Discord Bot Series (Part 3)</title>
      <dc:creator>Kevin Schildhorn</dc:creator>
      <pubDate>Wed, 06 Jul 2022 16:00:53 +0000</pubDate>
      <link>https://forem.com/kevinschildhorn/adding-tests-to-your-discord-bot-discord-bot-series-part-3-513</link>
      <guid>https://forem.com/kevinschildhorn/adding-tests-to-your-discord-bot-discord-bot-series-part-3-513</guid>
      <description>&lt;p&gt;Discord is a great platform for keeping in touch with friends, and adding a bot can enrich that experience. A bot can respond to messages, perform an action based on a command, or even add music to a voice chat.&lt;/p&gt;

&lt;p&gt;In this series we'll be going over how to create a small example bot by using the NPM module for DiscordJS in a KotlinJS project.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: This is the third post in a series of three posts, about how to use KotlinJS and NPM modules to create a full fledged project, in this case a Discord Bot.&lt;/p&gt;

&lt;h2&gt;
  
  
  Previously
&lt;/h2&gt;

&lt;p&gt;In my &lt;a href="https://dev.to/kevinschildhorn/using-kotlinjs-and-npm-to-create-a-discord-bot-discord-bot-series-part-2-1n6e"&gt;last post&lt;/a&gt; we went over how to add DiscordJS to our project, and start creating our bot. We added a listener for messages and replied to "hello" with a response of the computer name. I mentioned at the end that we weren't catching some edge cases with our response, and so in this post we'll be adding tests to our discord bot.&lt;/p&gt;

&lt;p&gt;As a refresher, in this series we're going to create a small discord bot that responds to a specific message with its computer name.&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating tests in kotlin
&lt;/h3&gt;

&lt;p&gt;This article assumes you understand some basics about adding tests in a kotlin project, specifically using the &lt;code&gt;kotlin.test&lt;/code&gt; library. You can see a helpful guide &lt;a href="https://www.jetbrains.com/help/idea/tdd-with-kotlin.html"&gt;from jetbrains&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding testing dependencies
&lt;/h2&gt;

&lt;p&gt;First off, we need to add dependencies to testing libraries. Make sure you have these dependencies in your &lt;code&gt;build.gradle&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nf"&gt;dependencies&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;testImplementation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;kotlin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"test"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="nf"&gt;testImplementation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;kotlin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"test-js"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Testing our logic
&lt;/h2&gt;

&lt;p&gt;In the &lt;a href="https://dev.to/kevinschildhorn/using-kotlinjs-and-npm-to-create-a-discord-bot-discord-bot-series-part-2-1n6e"&gt;last post&lt;/a&gt; we added logic to respond to "hello" messages from users in the channel. We now want to test and make sure our bot says hello only when it gets a correct message. We want to have them ignore capitalization and not respond to other bots (I mean they can if they want, this is just for the example). Feel free to add any other requirements you want!&lt;/p&gt;

&lt;h2&gt;
  
  
  Separating out code
&lt;/h2&gt;

&lt;p&gt;As it is now, we cannot test our code as it's just sitting inside the "messageCreate" callback. First we should move our code to a separate file. Let's make a new class called &lt;code&gt;HelloHandler&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;HelloHandler&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;handleMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"hello"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;channel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"..."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that we've moved our code to the handler we can update the existing code like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;helloHandler&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;HelloHandler&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"messageCreate"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
        &lt;span class="n"&gt;helloHandler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;handleMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we can call this code from a test. Let's make a quick test for testing a successful response:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;HelloGreetingTest&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="nd"&gt;@Test&lt;/span&gt;
    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;testHelloWorldSuccess&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;message&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;helloHandler&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;HelloHandler&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;helloHandler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;handleMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="c1"&gt;// assertTrue{ /* TODO: Assert the message was sent */ }&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Hopefully this seems straight forward, though you may notice that we  aren't asserting anything at the moment. This is because we don't have a way to track that a message has been sent, and we don't really want that in our &lt;code&gt;HelloHandler&lt;/code&gt;. For now it's commented out but we will update this in a later section.&lt;/p&gt;

&lt;h3&gt;
  
  
  Custom Messages
&lt;/h3&gt;

&lt;p&gt;Another thing you may notice is there's no way to set the content of the &lt;code&gt;Message&lt;/code&gt;, this is because the &lt;code&gt;Message&lt;/code&gt; class is defined externally, which means that the bot is expecting the definition from the external Js. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Even if we didn't want a custom message, you cannot use external classes in testing, because there are variables that are expected but undefined&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So how do we get around this? &lt;/p&gt;

&lt;h4&gt;
  
  
  Interfaces
&lt;/h4&gt;

&lt;p&gt;The easiest way I've found to test external classes is to create a common interface. Then we can have two classes implementing this interface: the external class we already have, and a mocking class. Let's create an interface for &lt;code&gt;Message&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;external&lt;/span&gt; &lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;MessageInterface&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;author&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;UserInterface&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;channel&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;ChannelInterface&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here we are defining the values we want to test, and we are setting it as external to match the existing &lt;code&gt;Message&lt;/code&gt; class. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: You don't need to define values you don't need, or else your interface would be massive for no good reason.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now we'll have:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;external&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Message&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;MessageInterface&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;author&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;
    &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;channel&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;TextChannel&lt;/span&gt;
    &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;data class&lt;/span&gt; &lt;span class="nc"&gt;MockMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;author&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;MockUser&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;channel&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;MockChannel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;MessageInterface&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I've gone ahead and created a &lt;code&gt;MockUser&lt;/code&gt; and a &lt;code&gt;MockChannel&lt;/code&gt;,  as we want to check the username for a bot name and check if the message was sent. These were created the same as the &lt;code&gt;MockMessage&lt;/code&gt;, by creating an interface and implementing it.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note that all other &lt;code&gt;Mock&lt;/code&gt; classes should be referencing the Interfaces you've created, not the external classes. &lt;em&gt;Don't forget&lt;/em&gt; to update the &lt;code&gt;HelloHandler&lt;/code&gt; to use the interface.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So now we can test a custom message, great! How do we make sure our bot sent a greeting as a response? &lt;/p&gt;

&lt;h3&gt;
  
  
  Checking Responses
&lt;/h3&gt;

&lt;p&gt;In order to test a response to an incoming message, we can create a &lt;code&gt;MockChannel&lt;/code&gt; to track when messages are sent. We don't want to actually send a message, just keep track of its status. I've created a &lt;code&gt;MockChannel&lt;/code&gt; that has a simple &lt;code&gt;Boolean&lt;/code&gt; that tells whether a message was sent.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MockChannel&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;ChannelInterface&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;sentMessage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Boolean&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;
        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;

    &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;MessageInterface&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Sent Message!"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;sentMessage&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So now we can assert that the &lt;code&gt;sentMessage&lt;/code&gt; variable is true or false, depending on our test. You can go a step further and keep a reference to the information for other tests, but for simplicity we'll stick to this. Now we can finally go back to the test function and update it with our new code.&lt;/p&gt;

&lt;h3&gt;
  
  
  Final Test
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;    &lt;span class="nd"&gt;@Test&lt;/span&gt;
    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;testHelloWorldSuccess&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;user&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;MockUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Kevin"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;channel&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;MockChannel&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;message&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;MockMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;channel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Hello"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;helloHandler&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;HelloHandler&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;helloHandler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;handleMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;assertTrue&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;channel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sentMessage&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here we have a working test. We create the mock information, create the Handler, then handle the message. Then we assert that the bot sent a message back! Now let's run the test, by either clicking the arrow next to the test and selecting &lt;code&gt;run&lt;/code&gt;, or calling &lt;code&gt;./gradlew test&lt;/code&gt; from the command line.&lt;/p&gt;

&lt;p&gt;It failed!&lt;/p&gt;

&lt;p&gt;Why did it fail? Let's check the &lt;code&gt;HelloHandler&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"hello"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;channel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"..."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It looks like we didn't take capitalization into account. Lets quickly change the check to &lt;code&gt;message.equals("hello", ignoreCase = true)&lt;/code&gt;, and run again.&lt;/p&gt;

&lt;p&gt;It passed!&lt;/p&gt;

&lt;p&gt;Excellent, our bot is working as we'd expect. We can go ahead and add more tests to make sure that other messages don't trigger a response, and catch other edge cases. Maybe we want to include exclamation points, or other languages, or we want to ignore hellos from users named Kevin. The possibilities are endless.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Congrats, You've tested your Discord Bot! You now have the basics to test all different messages and how your bot responds to them.&lt;/p&gt;

&lt;p&gt;Your bot should now be running, stable, and ready to join some channels. This is the last post of the series, at this point you should have everything you need to know to be able to run a simple Discord Bot in KotlinJs. There are many other references out there to expand your bot from here using KMP libraries. Hopefully this series has been helpful and given you an introduction into kotlinJs.&lt;/p&gt;

&lt;p&gt;Thanks for reading! Let me know in the comments if you have questions. Also, you can reach out to me at &lt;a href="https://twitter.com/KevinSchildhorn"&gt;@kevinschildhorn&lt;/a&gt; on Twitter.&lt;/p&gt;

</description>
      <category>kotlin</category>
      <category>javascript</category>
      <category>discord</category>
      <category>kotlinjs</category>
    </item>
    <item>
      <title>Using KotlinJS and NPM to create a Discord Bot - Discord Bot Series (Part 2)</title>
      <dc:creator>Kevin Schildhorn</dc:creator>
      <pubDate>Tue, 26 Apr 2022 18:37:31 +0000</pubDate>
      <link>https://forem.com/kevinschildhorn/using-kotlinjs-and-npm-to-create-a-discord-bot-discord-bot-series-part-2-1n6e</link>
      <guid>https://forem.com/kevinschildhorn/using-kotlinjs-and-npm-to-create-a-discord-bot-discord-bot-series-part-2-1n6e</guid>
      <description>&lt;p&gt;Discord is a great platform for keeping in touch with friends, and adding a bot can enrich that experience. A bot can respond to messages, perform an action based on a command, or even add music to a voice chat. &lt;/p&gt;

&lt;p&gt;In this series we'll be going over how to create a small example bot by using the NPM module for DiscordJS in a KotlinJS project.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: This is the second post in a series of three posts, about how to use KotlinJS and NPM modules to create a full fledged project, in this case a Discord Bot. You can find &lt;a href="https://dev.to/kevinschildhorn/how-to-use-npm-modules-in-kotlinjs-discord-bot-series-part-1-51ni"&gt;the first post here&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Previously
&lt;/h3&gt;

&lt;p&gt;In my last post we went over how to import node modules into our KotlinJS project, and created a simple project to test our imports. In this post we'll go over how to add DiscordJS to our project, and start creating our bot! Let's get started.&lt;/p&gt;

&lt;p&gt;As a refresher, in this series we're going to create a small discord bot that responds to a specific message with its computer name.&lt;/p&gt;

&lt;h3&gt;
  
  
  Registering a Discord Bot
&lt;/h3&gt;

&lt;p&gt;Before digging into code make sure you &lt;a href="https://discord.com/developers/applications" rel="noopener noreferrer"&gt;register a bot&lt;/a&gt;. Choose &lt;code&gt;New Application&lt;/code&gt;, add a name and click &lt;code&gt;create&lt;/code&gt;. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You can find more documentation on this process &lt;a href="https://discord.com/developers/docs/intro" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;After you create your bot you should see this screen.&lt;br&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%2Fm4g9pza8odqttlvklqab.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%2Fm4g9pza8odqttlvklqab.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click on the Bot tab on the left and in the &lt;code&gt;Build-a-bot&lt;/code&gt; section click &lt;code&gt;add bot&lt;/code&gt;. Once you've done this you'll have created a bot! Be sure to keep the token from this screen for use in the project.&lt;/p&gt;

&lt;p&gt;Also be sure to register your bot to your Channel using &lt;a href="https://discordjs.guide/preparations/adding-your-bot-to-servers.html#bot-invite-links" rel="noopener noreferrer"&gt;this guide&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  Initializing DiscordJS
&lt;/h3&gt;

&lt;p&gt;For our KotlinJS project we'll be using &lt;a href="https://discord.js.org/#/" rel="noopener noreferrer"&gt;DiscordJs&lt;/a&gt;, which is a well documented and widely used node module for discord.&lt;/p&gt;

&lt;p&gt;So first we'll add the dependency to the &lt;code&gt;build.gradle&lt;/code&gt; file like before:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dependencies {
    implementation(npm("discord.js", "13.6.0"))
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we can reference the &lt;a href="https://discordjs.guide/creating-your-bot/#creating-the-main-file" rel="noopener noreferrer"&gt;Creating the main file documents&lt;/a&gt; to find out how to launch the bot. We can see that the client is initialized, then it listens for messages, then finally calls to login.&lt;/p&gt;

&lt;p&gt;This is in Javascript but the kotlin implementation will look mostly the same. First we need to define the &lt;code&gt;Client&lt;/code&gt; class. So we'll go to the &lt;a href="https://discord.js.org/#/docs/discord.js/v13/class/Client" rel="noopener noreferrer"&gt;documentation for the client&lt;/a&gt;, and see what we need to define. We can see that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Client takes in &lt;code&gt;ClientOptions&lt;/code&gt; to init.&lt;/li&gt;
&lt;li&gt;Client inherits from &lt;code&gt;EventEmitter&lt;/code&gt;, which is how &lt;code&gt;once&lt;/code&gt; is called&lt;/li&gt;
&lt;li&gt;Client &lt;code&gt;login&lt;/code&gt; function takes in a string token and returns a string promise.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Init
&lt;/h4&gt;

&lt;p&gt;Before we go defining &lt;code&gt;ClientOptions&lt;/code&gt; and potentially going down the rabbit hole, we can see that it's defined as an object. In this case we can just pass a Json object in the constructor.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nd"&gt;@JsModule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"discord.js"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;external&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Json&lt;/span&gt;&lt;span class="p"&gt;?)&lt;/span&gt;

&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;GUILDS&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="c1"&gt;// 1 &amp;lt;&amp;lt; 0&lt;/span&gt;
&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;client&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Pair&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"intents"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;arrayOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;GUILDS&lt;/span&gt;&lt;span class="p"&gt;))))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The client takes in "intents", which is an array of just &lt;code&gt;Intents.FLAGS.GUILDS&lt;/code&gt;. If we follow the documentation from DiscordJS to the intents we can find their definition &lt;a href="https://discord.com/developers/docs/topics/gateway#list-of-intents" rel="noopener noreferrer"&gt;here&lt;/a&gt;. We see that &lt;code&gt;Intents.FLAGS.GUILDS&lt;/code&gt; is 1 &amp;lt;&amp;lt; 0 (or 1) so we'll pass that.&lt;/p&gt;

&lt;h4&gt;
  
  
  Once
&lt;/h4&gt;

&lt;p&gt;This function we can figure out by the simple call. This is just to let us know the client is ready, so we'll just print out a confirmation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nd"&gt;@JsModule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"discord.js"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;external&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Json&lt;/span&gt;&lt;span class="p"&gt;?)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;once&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cb&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Unit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Unit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;once&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ready"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello From ${computerName()}!"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can tell that it takes in a string, and returns a block. Since there's no input or output we can just use &lt;code&gt;Unit&lt;/code&gt;, which acts as &lt;code&gt;void&lt;/code&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Login
&lt;/h4&gt;

&lt;p&gt;Based on what we learned we can define this easily. The token we will pass in comes from your created bot on the discord site. This was the token that was grabbed from the beginning of this post.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nd"&gt;@JsModule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"discord.js"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;external&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Json&lt;/span&gt;&lt;span class="p"&gt;?)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;//...&lt;/span&gt;
    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;login&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;//Main.kt&lt;/span&gt;
&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;login&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"YOUR_TOKEN"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this case we can tell the function returns a Promise from the DiscordJS documentation.&lt;/p&gt;

&lt;h4&gt;
  
  
  Running your bot
&lt;/h4&gt;

&lt;p&gt;At this point all the calls should be in, your &lt;code&gt;Main.kt&lt;/code&gt; should look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;GUILDS&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="c1"&gt;// 1 &amp;lt;&amp;lt; 0&lt;/span&gt;
&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;client&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Pair&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"intents"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;arrayOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;GUILDS&lt;/span&gt;&lt;span class="p"&gt;))))&lt;/span&gt;
&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;once&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ready"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello From ${computerName()}!"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;login&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you run it you should be able to see your computer name in the terminal and your bot should be online!&lt;/p&gt;

&lt;h2&gt;
  
  
  Responding to Messages
&lt;/h2&gt;

&lt;p&gt;This is great that the bot is running, however right now it's not doing anything. So let's get the bot to respond to a message.&lt;/p&gt;

&lt;h4&gt;
  
  
  Note: intent
&lt;/h4&gt;

&lt;p&gt;In order for the bot to get messages, the client needs to register another intent, &lt;code&gt;GUILDS_MESSAGES&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const val GUILDS = 1 // 1 &amp;lt;&amp;lt; 0
const val GUILDS_MESSAGES = 512  //1 &amp;lt;&amp;lt; 9
val client = Client(json(Pair("intents", arrayOf(GUILDS, GUILDS_MESSAGES))))

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Defining Messages
&lt;/h3&gt;

&lt;p&gt;So first we'll quickly define a message, as that's what's passed in. From &lt;a href="https://discord.js.org/#/docs/discord.js/v13/class/Message" rel="noopener noreferrer"&gt;the docs&lt;/a&gt; we can see a message contains a &lt;code&gt;content&lt;/code&gt; string, the &lt;code&gt;author&lt;/code&gt;, and the &lt;code&gt;channel&lt;/code&gt; it was sent in. we're going to use the &lt;code&gt;channel&lt;/code&gt; to send the reply to, and use the &lt;code&gt;author&lt;/code&gt; to create a personalized message.&lt;br&gt;
To make things quicker I've defined what we need from the documentation below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;external&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Message&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;author&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;channel&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;TextChannel&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;external&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;avatar&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;external&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TextChannel&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Listening for Messages
&lt;/h3&gt;

&lt;p&gt;With that being set we can then go to the &lt;code&gt;Client&lt;/code&gt; definition and add a listener for the message:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;external&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Json&lt;/span&gt;&lt;span class="p"&gt;?)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cb&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Unit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So now we can listen for "message" events (or rather "messageCreate" events defined in the &lt;a href="https://discordjs.guide/additional-info/changes-in-v13.html#naming-conventions" rel="noopener noreferrer"&gt;migration guide&lt;/a&gt;).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"messageCreate"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To go even further let's implement our desired code. We'll check to see if someone says hello, and if they do respond with their name and the bots computer name.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"messageCreate"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"hello"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;channel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello ${message.author.username} From ${computerName()}!"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now build and run the bot, and say hello! You should see a response like so.&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%2F8vuv1uevzgl7psl86lqf.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%2F8vuv1uevzgl7psl86lqf.png" alt="Discord Response"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Congrats, You've made a working Discord Bot! You now have the basics to expand your bot to respond to lots of different messages in many ways.&lt;/p&gt;

&lt;p&gt;After playing around with the bot you may realize that there are some uncaught issues, for example the bot won't respond if there's a space in the message or if "Hello" is capitalized. In the next post of this series, we'll go over how we can write tests to catch these issues and make sure our bot works the way we expect.&lt;/p&gt;

&lt;p&gt;Thanks for reading! Let me know in the comments if you have questions. Also, you can reach out to me at &lt;a href="https://twitter.com/KevinSchildhorn" rel="noopener noreferrer"&gt;@kevinschildhorn&lt;/a&gt; on Twitter.&lt;/p&gt;

</description>
      <category>kotlin</category>
      <category>javascript</category>
      <category>discord</category>
      <category>kotlinjs</category>
    </item>
    <item>
      <title>How to use NPM modules in Kotlin/JS - Discord Bot Series (Part 1)</title>
      <dc:creator>Kevin Schildhorn</dc:creator>
      <pubDate>Tue, 12 Apr 2022 16:05:14 +0000</pubDate>
      <link>https://forem.com/kevinschildhorn/how-to-use-npm-modules-in-kotlinjs-discord-bot-series-part-1-51ni</link>
      <guid>https://forem.com/kevinschildhorn/how-to-use-npm-modules-in-kotlinjs-discord-bot-series-part-1-51ni</guid>
      <description>&lt;p&gt;KotlinJS is an exciting new method of creating javascript projects that combines the benefits of javascripts speed and versatility and kotlins strong typing and conciseness. Plus the ability to easily add testing and even share code across platforms. &lt;/p&gt;

&lt;p&gt;Since KotlinJS transpiles kotlin code to javascript it gives you all the advantages of executing a NodeJS program, while enjoying the benefits of the kotlin language.&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%2Fi.redd.it%2Fnj02sbmhlq861.jpg" 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%2Fi.redd.it%2Fnj02sbmhlq861.jpg"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There are &lt;a href="https://kotlinlang.org/docs/js-overview.html#use-cases-for-kotlin-js" rel="noopener noreferrer"&gt;many use cases&lt;/a&gt; for KotlinJS and in this series we'll be creating a Discord bot. Discord is a great platform for keeping in touch with friends, and adding a bot can enrich that experience. We can create this bot by using the NPM module for &lt;code&gt;DiscordJS&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;So how do you create a discord bot using KotlinJS? Well since this is a somewhat large topic &lt;strong&gt;this post will be the first in a series of three posts&lt;/strong&gt;: &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;In this post we'll go over how kotlinJS uses node modules. This is more of an intro to KotlinJS and doesn't cover Discord.&lt;/li&gt;
&lt;li&gt;In the next post we'll go over implementing DiscordJS and responding to messages.&lt;/li&gt;
&lt;li&gt;In the last post we'll add some unit tests to make sure the bot works as expected before deploying.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;p&gt;By the end of this series we'll have a small discord bot that responds to a specific message with the computer name it's running on. In this first part the bot will just print to console on startup, but responding to messages will come later in another part.&lt;/p&gt;

&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Some Kotlin Experience&lt;/li&gt;
&lt;li&gt;Some JS Experience&lt;/li&gt;
&lt;li&gt;IntelliJ IDEA (I'm using Community Edition)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  New Project
&lt;/h2&gt;

&lt;p&gt;To get started, first follow the &lt;a href="https://kotlinlang.org/docs/js-project-setup.html" rel="noopener noreferrer"&gt;js project setup&lt;/a&gt; on the kotlin site. (Be sure to choose &lt;code&gt;NodeJS Application&lt;/code&gt;, not &lt;code&gt;browser&lt;/code&gt;)&lt;/p&gt;

&lt;p&gt;This will create a project with a &lt;code&gt;Main.kt&lt;/code&gt; file and a greeting function. To run the project, call &lt;code&gt;./gradlew run&lt;/code&gt; in the terminal. You should see &lt;code&gt;Hello, YOUR_BOT_NAME&lt;/code&gt; in the terminal!&lt;/p&gt;

&lt;h3&gt;
  
  
  Importing Node Modules in KotlinJS
&lt;/h3&gt;

&lt;p&gt;Lets start off by getting familiar with how KotlinJS handles Node Modules.&lt;/p&gt;

&lt;p&gt;To have the bot send a message with its name, let's use a simple node module called &lt;a href="https://www.npmjs.com/package/computer-name#usage" rel="noopener noreferrer"&gt;computer-name&lt;/a&gt;. As mentioned in the project setup, node modules are implemented as dependencies in the &lt;code&gt;build.gradle&lt;/code&gt; file. No need for &lt;code&gt;require&lt;/code&gt; or special imports. Add this line to your dependencies.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight gradle"&gt;&lt;code&gt;&lt;span class="k"&gt;dependencies&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;implementation&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;npm&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"computer-name"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"0.1.0"&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you try to call &lt;code&gt;computerName()&lt;/code&gt; now, you'll get an &lt;code&gt;Unresolved reference&lt;/code&gt; error. This is because of a crucial difference between javascript and kotlin:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Javascript is loosely typed and kotlin is strongly typed&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So in order to use this function we need to define it by using external functions.&lt;/p&gt;

&lt;h4&gt;
  
  
  External
&lt;/h4&gt;

&lt;p&gt;The kotlin docs mention the &lt;a href="https://kotlinlang.org/docs/js-interop.html#external-modifier" rel="noopener noreferrer"&gt;External Modifier&lt;/a&gt;, which is used to declare pure javascript code. This tells the compiler that we're expecting this class or function to be defined externally by the node module. Whenever we want to use a module we have to define functions and classes. An important note about this is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You only need to define what you are using&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So you don't need to define the entire module, just what you need to reference.&lt;/p&gt;

&lt;p&gt;So for us to use &lt;code&gt;computerName()&lt;/code&gt; we need to define it and tell the compiler where it's defined, like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nd"&gt;@JsModule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"computer-name"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;external&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;computerName&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that we also need to annotate the function with the module name or else we'll hit a runtime error(&lt;code&gt;compilerReferenceError&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;This definition was easy to figure out based on the npm page, but as we'll see you may have to dig into the documentation and source code of node modules to find the original definition.&lt;/p&gt;

&lt;h5&gt;
  
  
  Module System
&lt;/h5&gt;

&lt;p&gt;Another thing we have to define is the module system. You can find more information &lt;a href="https://kotlinlang.org/docs/js-modules.html" rel="noopener noreferrer"&gt;here&lt;/a&gt;, but in short KotlinJS supports &lt;code&gt;UMD&lt;/code&gt;, &lt;code&gt;AMD&lt;/code&gt; and &lt;code&gt;commonJS&lt;/code&gt; systems. &lt;code&gt;commonJS&lt;/code&gt; is widely used for NodeJS so we'll add &lt;code&gt;useCommonJs()&lt;/code&gt; to our &lt;code&gt;build.gradle&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight gradle"&gt;&lt;code&gt;&lt;span class="n"&gt;kotlin&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;js&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IR&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;useCommonJs&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now try adding &lt;code&gt;println(computerName())&lt;/code&gt; to &lt;code&gt;main()&lt;/code&gt; and run the project.&lt;/p&gt;

&lt;p&gt;You should see the name of your computer in the terminal! congrats! Now let's look at importing classes.&lt;/p&gt;

&lt;h4&gt;
  
  
  Importing NodeJS Classes
&lt;/h4&gt;

&lt;p&gt;So far we've covered importing a function by using &lt;code&gt;computerName()&lt;/code&gt; as an example, but that's a small module with one function. It's also important to be able to import classes and interfaces from modules to use in our project.&lt;/p&gt;

&lt;p&gt;For this example we'll use &lt;a href="https://www.npmjs.com/package/youtube-search" rel="noopener noreferrer"&gt;youtube-search&lt;/a&gt;, which is a small module that lets you search for youtube videos (&lt;strong&gt;Note:&lt;/strong&gt; You won't get results without a youtube api key, but the calls will still work which is all we need). From the npm page you can see there's a search function that returns custom results. So how do we define this in kotlin?&lt;/p&gt;

&lt;p&gt;While there is definition in this module, we'll need to go to &lt;a href="https://www.npmjs.com/package/youtube-search" rel="noopener noreferrer"&gt;the repository&lt;/a&gt; to get more information. Lucky for us this module is conveniently written in typescript so it's very easy to find the definition, which is written in &lt;code&gt;index.d.ts&lt;/code&gt;.&lt;/p&gt;

&lt;h5&gt;
  
  
  Dukat
&lt;/h5&gt;

&lt;p&gt;Before we get into manually converting typescript to kotlin code, I should mention there is a tool to do this automatically called &lt;a href="https://kotlinlang.org/docs/js-external-declarations-with-dukat.html" rel="noopener noreferrer"&gt;Dukat&lt;/a&gt;. It is a powerful tool that can help with easy conversion, however for this post I want to go over how to manually convert typescript to kotlin so that you have a good understanding of how it works.&lt;/p&gt;

&lt;h5&gt;
  
  
  Manually Converting Typescript
&lt;/h5&gt;

&lt;p&gt;From the repo open the &lt;code&gt;index.d.ts&lt;/code&gt; file and scroll to the bottom to see the search function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;declare&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;term&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;search&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;YouTubeSearchOptions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;cb&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;search&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;YouTubeSearchResults&lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="nx"&gt;pageInfo&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;search&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;YouTubeSearchPageResults&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt;
&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;results&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;search&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;YouTubeSearchResults&lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="na"&gt;pageInfo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;search&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;YouTubeSearchPageResults&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can see it takes in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a string&lt;/li&gt;
&lt;li&gt;a custom options class&lt;/li&gt;
&lt;li&gt;a callback that returns an optional error, results array, and pageInfo array&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The fuction then returns a Promise. Luckily kotlin already defines errors in the  &lt;code&gt;stdlib&lt;/code&gt; and it also includes an import for promises:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;kotlin.js.Promise&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So the difficult parts are the Results and options. We can see that &lt;code&gt;YouTubeSearchResults&lt;/code&gt; and &lt;code&gt;YouTubeSearchPageResults&lt;/code&gt; are interfaces, so we can easily define them like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nc"&gt;JsModule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"youtube-search"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;// Note that for multiple definitions in the same file you can use @file&lt;/span&gt;

&lt;span class="k"&gt;external&lt;/span&gt; &lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;YouTubeSearchPageResults&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;totalResults&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;
  &lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;external&lt;/span&gt; &lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;YouTubeSearchResults&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;
  &lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now for &lt;code&gt;YouTubeSearchOptions&lt;/code&gt; we could do the same approach, but there's another option we can use.&lt;/p&gt;

&lt;h5&gt;
  
  
  Json
&lt;/h5&gt;

&lt;p&gt;We can simply use a &lt;code&gt;Json&lt;/code&gt; class object, which acts similarly to a &lt;code&gt;HashMap&lt;/code&gt;. This way we can just pass in what we want without defining the entire interface. This also works well if you can't find a clear definition of an object being passed in.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;kotlin.js.Json&lt;/span&gt;

&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;options&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nc"&gt;Pair&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"maxResults"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nc"&gt;Pair&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"key"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;YOUR_YOUTUBE_API_KEY&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// Leave this blank if you don't have one&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So from all of this, we can then create the external search function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nd"&gt;@JsModule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"youtube-search"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;external&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;term&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Json&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;cb&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;?,&lt;/span&gt;
        &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;YouTubeSearchResults&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;?,&lt;/span&gt;
        &lt;span class="n"&gt;pageInfo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;YouTubeSearchPageResults&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Unit&lt;/span&gt; &lt;span class="c1"&gt;// Unit acts as Void in kotlin&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Json&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"your search"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pageInfo&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Youtube callback $err, $result, $pageInfo\n"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Try adding this to your project and run. If you don't have an api key you should see this in the terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Youtube callback Error: Request failed with status code 403, null, null
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you &lt;em&gt;do&lt;/em&gt; have the api key you should see this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Youtube callback null, [...], [object Object]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Congratulations you have a working KotlinJS project with imported NPM modules! In the next post we'll go over the specifics of creating our Discord bot using DiscordJS.&lt;/p&gt;

&lt;p&gt;Thanks for reading! Let me know in the comments if you have questions. Also, you can reach out to me at &lt;a href="https://twitter.com/KevinSchildhorn" rel="noopener noreferrer"&gt;@kevinschildhorn&lt;/a&gt; on Twitter.&lt;/p&gt;

</description>
      <category>kotlin</category>
      <category>javascript</category>
      <category>discord</category>
      <category>kotlinjs</category>
    </item>
    <item>
      <title>Dividing Kotlin Multiplatform work in teams</title>
      <dc:creator>Kevin Schildhorn</dc:creator>
      <pubDate>Mon, 28 Feb 2022 19:02:28 +0000</pubDate>
      <link>https://forem.com/touchlab/dividing-kotlin-multiplatform-work-in-teams-2cad</link>
      <guid>https://forem.com/touchlab/dividing-kotlin-multiplatform-work-in-teams-2cad</guid>
      <description>&lt;p&gt;Kotlin Multiplatform is a great way to share code between multiple platforms, but this new approach can be confusing to  navigate. There's a variety of sourceSets for platform specific code, plus intermediate sourceSets(such as &lt;code&gt;mobileMain&lt;/code&gt;) that cover multiple platforms at once. One of the difficulties that can arise is how to divide work among your team.&lt;/p&gt;

&lt;p&gt;This gets more difficult the larger your team is and the more platforms you're developing for. It sounds straightforward at first: the iOS developer works on the iOS code, the javascript works on the JS code, and so on. In practice however, you may run into unforeseen challenges. &lt;/p&gt;

&lt;h2&gt;
  
  
  Challenges
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Languages&lt;/strong&gt;: KMP is of course written in Kotlin, but if you have iOS developers or Javascript developers that only know Swift or JS, then there's going to be a learning curve. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Android Bias&lt;/strong&gt;: You may think if you're writing Kotlin code then get the Android developer to do it. Not only are you pushing all the work on your Android devs, but there's chances for Android biases in their code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Conflicts&lt;/strong&gt;: If you have all devs working on their own platforms at the same time, that can lead to conflicts when merging PRs and arguments about the best approach.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Assigning Tasks&lt;/strong&gt;: Beyond writing code there is an issue of issue tracking. When assigning work how do you know who's working on what layers? An Android task could include shared code. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So how do you assign work to multiple developers effectively, without causing the team to step on each others toes?&lt;/p&gt;

&lt;h2&gt;
  
  
  Our Approach
&lt;/h2&gt;

&lt;p&gt;The best approach we've found is to split up the work into two camps: &lt;/p&gt;

&lt;p&gt;&lt;code&gt;PlatformSpecificUI&lt;/code&gt; and &lt;code&gt;KMP&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;For example, if our project is written for Android, iOS and JS we'll have four groups:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;androidUI&lt;/code&gt;, &lt;code&gt;iosUI&lt;/code&gt;, &lt;code&gt;jsUI&lt;/code&gt; and &lt;code&gt;KMP&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This again may sound straightforward, but there are some things worth mentioning. First let's visualize the layers.&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%2Foit0vnm248d8bjgtwzge.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%2Foit0vnm248d8bjgtwzge.png" alt="KMP Layers"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here we can see the different layers in our example. We have:&lt;br&gt;
&lt;code&gt;androidMain, iOSMain, jsMain, mobileMain and commonMain&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;So in our example we have the four groups and their work:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;androidUI&lt;/strong&gt; works in &lt;code&gt;androidMain&lt;/code&gt; and the Android layer&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;iOSUI&lt;/strong&gt; works in &lt;code&gt;iosMain&lt;/code&gt; and the iOS layer&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;jsUI&lt;/strong&gt; works in the js layer&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;KMP&lt;/strong&gt; works in &lt;code&gt;commonMain, mobileMain, and jsMain&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&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%2Fsggerzv3gf59bdw4hx79.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%2Fsggerzv3gf59bdw4hx79.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With this approach all the platforms are dependent on the &lt;strong&gt;KMP&lt;/strong&gt; groups work(The common layer).&lt;/p&gt;

&lt;h2&gt;
  
  
  An Example
&lt;/h2&gt;

&lt;p&gt;So as an example, we want to add a new feature: &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A new screen that fetches an image from an api and displays it when a button is pressed. This feature will be on every platform, and it's going to be tracked via an issue tracker.&lt;/p&gt;
&lt;/blockquote&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%2Fbwk943d662p8ikaezmqd.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%2Fbwk943d662p8ikaezmqd.png" alt="Division of Work"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Above we have an example of what your architecture might look like a lot at first, but let's go over it.&lt;/p&gt;

&lt;p&gt;In Android, we have:&lt;br&gt;
&lt;code&gt;Fragment -&amp;gt; ViewModel -&amp;gt; Coordinator -&amp;gt; Repo -&amp;gt; network api&lt;/code&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A button is pressed in the Fragment, an action is sent to the ViewModel which using a coroutine calls the coordinator. The coordinator then gets the image from the repository and processes it to bring back to the ui. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A similar flow can be seen for iOS and Javascript, but with their own implementations. In this example we have the interfaces &lt;code&gt;RepositoryCommon&lt;/code&gt; and &lt;code&gt;Api&lt;/code&gt;, which are then implemented platform specific.&lt;/p&gt;

&lt;h4&gt;
  
  
  Splitting the work
&lt;/h4&gt;

&lt;p&gt;You may be asking why the &lt;code&gt;jsUI&lt;/code&gt; doesn't work in &lt;code&gt;jsMain&lt;/code&gt;, while the other two groups work in their respective sourceSets?&lt;/p&gt;

&lt;p&gt;It's better to think of it in this way:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The KMP group is working on the common code and its implementations&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In the example above the KMP group is working on the CommonRepository, as well as the implementations. They even work on the next level up(The Coordinator and js Service). The Coordinator and service are still in business logic, and despite having their own platforms they are still using shared libraries.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this approach
&lt;/h2&gt;

&lt;p&gt;This approach prevents some of the issues mentioned prior:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No platform delays&lt;/strong&gt;: Since all the platform specific work relies on the &lt;strong&gt;KMP&lt;/strong&gt; groups work, there is no one platform that has an extra workload and all platforms start at the same starting line.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Increasing Platform Knowledge&lt;/strong&gt;: The &lt;strong&gt;KMP&lt;/strong&gt; group creating the groundwork for all the different platforms means that they can grow their knowledge of all the platforms in development.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No conflicts&lt;/strong&gt;: All the platforms relying on the common layer ensures that all the platforms are in agreement with the business logic before working on the platform-specific code. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;:With this method all platform developers would review and approve this foundational code, to make sure it fits all their requirements. This also helps maintain feature parity across platforms, and avoid platform biases.&lt;/p&gt;

&lt;h4&gt;
  
  
  Platform Equality
&lt;/h4&gt;

&lt;p&gt;One key issue that I think a lot of KMP developers struggle with is platform equality. It's easy to start with the Android implementation since it's in Kotlin and the KMP library is easily debuggable. This approach puts a focus on Android, and other platforms can lose out because of it. They're seen as afterthoughts and don't get as much focus as Android.&lt;/p&gt;

&lt;p&gt;A small example is that lists are not supported in KotlinJS, so if the developer starts using lists in the android implementation they may not notice until the JS project is built.&lt;/p&gt;

&lt;p&gt;One of the advantages to having one group work in KMP is that it gives even attention to every platform, as the KMP developer has to write code with all the platforms in mind.&lt;/p&gt;

&lt;h2&gt;
  
  
  Layer Intermingling
&lt;/h2&gt;

&lt;p&gt;There are some issues that may come up, especially when working between business and UI logic. Developers have different levels of experience in languages and Platforms, so crossing the bridge between logic layers can be tricky. &lt;/p&gt;

&lt;p&gt;Examples of these issues are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Platform specifics&lt;/strong&gt;: shared code can still call platform libraries, such as &lt;code&gt;iosMain&lt;/code&gt; calling &lt;code&gt;Foundation&lt;/code&gt;, so Kotlin developers may not know these libraries.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Different Languages&lt;/strong&gt;: In our approach platform developers are working in Kotlin sourceSets(i.e. an iOS developer has to work in iOSMain). An iOS dev may be familiar with &lt;code&gt;UIKit&lt;/code&gt; and Swift, but is unsure how to write the code in Kotlin.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In cases like this where a there is an intersection between shared code and platform specific code, pair programming is a great option. Not only does it ensure that the code meets both sides' requirements, but it encourages knowledge sharing.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;KMP&lt;/strong&gt; developer working on the shared code can create a base implementation, then work alongside the platform-specific developer to find the best approach to the issue. All the while both developers are sharing their knowledge of Kotlin and platform-specific code. &lt;/p&gt;

&lt;h2&gt;
  
  
  Downsides
&lt;/h2&gt;

&lt;p&gt;The main downside to this approach, is that the KMP dev cannot easily test their implementation. For example if they are writing a network call and tries to serialize it into what they think the format is, it's only until one of the platform developers implements the call that they will realize it's incorrect. &lt;/p&gt;

&lt;p&gt;One way to get ahead of this issue is to create tests whenever possible. Testing network layers can be tricky, but writing some form of test to make sure your code works for the platforms will help nip these issues in the bud.&lt;/p&gt;

&lt;p&gt;If there is an issue like this, then it should be communicated with the team so that the other platform developers know that there is an issue, and the team can work to resolve the bug.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Every team is different, and every project is different. While this approach may not suit everyone, at Touchlab we have found that it can help organize work and increase knowledge sharing. &lt;/p&gt;

&lt;p&gt;Thanks for reading! Let me know in the comments if you have questions. Also, you can reach out to me at &lt;a class="mentioned-user" href="https://dev.to/kevinschildhorn"&gt;@kevinschildhorn&lt;/a&gt; on &lt;a href="https://twitter.com/KevinSchildhorn" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;, or on the Kotlin Slack. And if you find all this interesting, maybe you'd like to &lt;a href="https://touchlab.co/contact-us/" rel="noopener noreferrer"&gt;work with&lt;/a&gt; or &lt;a href="https://touchlab.co/careers-3/" rel="noopener noreferrer"&gt;work at&lt;/a&gt; Touchlab.&lt;/p&gt;

</description>
      <category>kotlin</category>
      <category>kotlinmultiplatform</category>
      <category>productivity</category>
      <category>mobile</category>
    </item>
    <item>
      <title>Kotlin 1.4 suspend functions</title>
      <dc:creator>Kevin Schildhorn</dc:creator>
      <pubDate>Wed, 24 Jun 2020 16:33:16 +0000</pubDate>
      <link>https://forem.com/touchlab/kotlin-1-4-suspend-functions-209</link>
      <guid>https://forem.com/touchlab/kotlin-1-4-suspend-functions-209</guid>
      <description>&lt;p&gt;It's that time again, when a new version of Kotlin is released and a new promising Kotlin Multiplatform feature is announced. Much like &lt;a href="https://dev.to/touchlab/multiple-kotlin-frameworks-in-an-application-34e9"&gt;my last past post&lt;/a&gt; I saw this new release of 1.4-M2 with a new feature and was curious how it would work in practice. This time it's the addition of:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Support for Kotlin’s suspending functions in Swift and Objective-C&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Again, this is a great feature but there isn't a lot of information to go off of in the release notes. Sure it shows some sample code and mentions you can only run it in the main thread, but the implementation seems simplified and leaves out customization options. This is most likely because there are many useful features of coroutines (like Jobs and Dispatchers) that are part of kotlinx libraries and are not available in the stdlib. So with all that said, how do these suspending functions work in swift and objective-c? Will this be useful in your Kotlin Multiplatform project? Let's find out.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Code
&lt;/h2&gt;

&lt;p&gt;So first off, I've made a simple example of this new feature on Github, &lt;a href="https://github.com/touchlab/NativeSuspendExample"&gt;which can be found here&lt;/a&gt;. In this example there is a suspend function in the shared kmp library called &lt;code&gt;helloWorld&lt;/code&gt; in the class &lt;code&gt;SuspendSampleHandler&lt;/code&gt;. This function is called twice in the ViewController of the matching XCode project to showcase a success and failure. Let's jump into the details of how this function is called.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kt"&gt;SuspendSampleHandler&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;helloWorld&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;showError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="nv"&gt;completionHandler&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;
   &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;errorReal&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;errorReal&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;localizedDescription&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
   &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;resultReal&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;resultReal&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Prior to 1.4-M2, suspend functions would not generate Objective-C output, and were not callable from iOS. In 1.4, suspend functions will generate Objective-C functions which take in a block with two arguments: a result and an NSError, both of which are Optional. &lt;/p&gt;

&lt;p&gt;So in this case the result will be the successful result, and the error would be any errors or throwables that may have happened during the call. In the example code we have the function returning either a success or a failure based on the &lt;code&gt;showError&lt;/code&gt; boolean variable.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Results
&lt;/h2&gt;

&lt;p&gt;This method of calling suspend functions does work successfully, however a lot of this implementation still remains a mystery. Even after testing the sample and attempting to dig into the source code, I'm unable to clarify how the suspend function is called. I cannot find a way to pass in Scopes or Dispatchers, or having it return a Job. Another thing I couldn't get working are &lt;code&gt;Flow&lt;/code&gt;'s. While they did compile successfully in XCode I was unable to get them to work during runtime. This could be a mistake on my part, but as far as I could tell there wasn't a successful way to use &lt;code&gt;Flow&lt;/code&gt; from iOS.&lt;/p&gt;

&lt;p&gt;In short, I've come to these conclusions about this feature:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The Scope and Dispatcher is unknown&lt;/li&gt;
&lt;li&gt;There is no way to supply a Context or Scope&lt;/li&gt;
&lt;li&gt;There is no way to cancel a coroutine in flight&lt;/li&gt;
&lt;li&gt;There is no real &lt;code&gt;Flow&lt;/code&gt; support&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Should you use it
&lt;/h2&gt;

&lt;p&gt;The answer isn't a simple yes or no, it depends on your usage. &lt;/p&gt;

&lt;p&gt;This new feature is a nice straight forward way to call suspend functions from iOS, so if you're going the simplified route then I'd recommend it. &lt;/p&gt;

&lt;p&gt;On the other hand this feature is missing a lot of options for customization that you would get with kotlinx coroutines. Things like the ability to cancel jobs, set scopes, use Flows, and set contexts. With this new language-level style you don't get these features.&lt;/p&gt;

&lt;p&gt;With all that said, I most likely won't be using this feature. We are able to create something similar to this approach while still having the options for customization and &lt;code&gt;Flow&lt;/code&gt;'s. For an example you can look at how we implement suspend functions in our &lt;a href="https://github.com/touchlab/KaMPKit/blob/master/shared/src/iosMain/kotlin/co/touchlab/kampkit/NativeViewModel.kt"&gt;KaMPKit sample&lt;/a&gt;. This implementation supports a custom scope, Flows, and could easily be updated to support cancellation. &lt;/p&gt;

&lt;p&gt;Our approach includes non-suspending functions which launches a predefined &lt;code&gt;kotlinx.coroutine&lt;/code&gt; scope. The results are then sent to a callback that is set when creating the object. If you want to avoid some boilerplate and have consistent interfaces, you could expose suspend functions to the compiler and wrap those calls in your own &lt;code&gt;kotlinx.coroutines&lt;/code&gt; scope once you’re on the Kotlin side. &lt;br&gt;
Writing your own interface involves a little more work, but you gain much better control over the concurrency lifecycle.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;It's nice that Kotlin added support for suspend functions in swift and objective-c. It provides a simplified and easy to use implementation for suspend functions, but as a trade-off you lose a lot of customization and control in the process. I personally would not use this new feature, and would instead choose a custom implementation in the common code.  &lt;/p&gt;

</description>
      <category>kotlin</category>
      <category>kotlinmultiplatform</category>
      <category>ios</category>
      <category>development</category>
    </item>
    <item>
      <title>Multiple Kotlin Frameworks in an Application</title>
      <dc:creator>Kevin Schildhorn</dc:creator>
      <pubDate>Fri, 17 Apr 2020 19:39:55 +0000</pubDate>
      <link>https://forem.com/touchlab/multiple-kotlin-frameworks-in-an-application-34e9</link>
      <guid>https://forem.com/touchlab/multiple-kotlin-frameworks-in-an-application-34e9</guid>
      <description>&lt;p&gt;Recently Kotlin 1.3.70 was released, adding many improvements and fixes for Kotlin. One of the most exciting improvements is the one mentioned below:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Support for multiple Kotlin frameworks in a single application&lt;/p&gt;

&lt;p&gt;Previously, there was a known issue that an application could not use more than one dynamic Kotlin/Native framework because Obj-C classes defined in runtime were conflicting, coming from different instances of runtime. We’ve fixed this in 1.3.70, so now an application can use multiple Kotlin/Native frameworks. Any common dependencies are present in the frameworks under different Swift modules (and with different Obj-C prefixes).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is an exciting update! Prior to 1.3.70 you had to have all of your Kotlin code in one framework, essentially having a monolithic library. Now this update supports multiple frameworks which opens the door for library developers to publish KMP built frameworks for usage in iOS.&lt;/p&gt;

&lt;p&gt;This is a great feature, but as your mind starts to wander about the possibilities of having multiple frameworks you may begin to have some questions about the specifics of implementing these frameworks. You may be thinking things like: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Does this include static frameworks? The notes only mention dynamic frameworks&lt;/li&gt;
&lt;li&gt;Are there any naming restrictions? What if I have multiple classes or modules with the same name?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And for that matter..&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How well do the modules talk to each other? Do the multiple frameworks work together easily?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We had these questions too and we decided to do some research to try and find answers. So here are some of the results to our questions about multiple Kotlin frameworks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Does this include static frameworks?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Answer&lt;/strong&gt;: yes and no. While asking around the Kotlin slack (Which you can join via this link if you haven’t already &lt;a href="https://surveys.jetbrains.com/s3/kotlin-slack-sign-up" rel="noopener noreferrer"&gt;here&lt;/a&gt;), and researching some of the existing Github issues, we’ve found out that the 1.3.70 supports static libraries...when they’re set to release. Currently multiple debug static frameworks are not supported in 1.3.70, as mentioned in this comment on the Kotlin-Native Repo in &lt;a href="https://github.com/JetBrains/kotlin-native/issues/2423#issuecomment-591504566" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;br&gt;
&lt;strong&gt;Note&lt;/strong&gt;: Multiple debug static frameworks should work in Kotlin 1.4&lt;/p&gt;

&lt;p&gt;Just to confirm this, and do some testing ourselves, we tested a bunch of different configurations to see what does and doesn’t work. Our findings are below&lt;/p&gt;

&lt;p&gt;Results&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Library 1&lt;/th&gt;
&lt;th&gt;Build Type&lt;/th&gt;
&lt;th&gt;Library 2&lt;/th&gt;
&lt;th&gt;Build Type&lt;/th&gt;
&lt;th&gt;Result&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Dynamic&lt;/td&gt;
&lt;td&gt;Debug&lt;/td&gt;
&lt;td&gt;Dynamic&lt;/td&gt;
&lt;td&gt;Debug&lt;/td&gt;
&lt;td&gt;Success&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dynamic&lt;/td&gt;
&lt;td&gt;Release&lt;/td&gt;
&lt;td&gt;Dynamic&lt;/td&gt;
&lt;td&gt;Release&lt;/td&gt;
&lt;td&gt;Success&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dynamic&lt;/td&gt;
&lt;td&gt;Debug&lt;/td&gt;
&lt;td&gt;Dynamic&lt;/td&gt;
&lt;td&gt;Release&lt;/td&gt;
&lt;td&gt;Success&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Static&lt;/td&gt;
&lt;td&gt;Debug&lt;/td&gt;
&lt;td&gt;Static&lt;/td&gt;
&lt;td&gt;Debug&lt;/td&gt;
&lt;td&gt;Failure*&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Static&lt;/td&gt;
&lt;td&gt;Release&lt;/td&gt;
&lt;td&gt;Static&lt;/td&gt;
&lt;td&gt;Release&lt;/td&gt;
&lt;td&gt;Success&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Static&lt;/td&gt;
&lt;td&gt;Debug&lt;/td&gt;
&lt;td&gt;Static&lt;/td&gt;
&lt;td&gt;Release&lt;/td&gt;
&lt;td&gt;Success&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dynamic&lt;/td&gt;
&lt;td&gt;Debug&lt;/td&gt;
&lt;td&gt;Static&lt;/td&gt;
&lt;td&gt;Debug&lt;/td&gt;
&lt;td&gt;Success&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dynamic&lt;/td&gt;
&lt;td&gt;Release&lt;/td&gt;
&lt;td&gt;Static&lt;/td&gt;
&lt;td&gt;Release&lt;/td&gt;
&lt;td&gt;Success&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dynamic&lt;/td&gt;
&lt;td&gt;Debug&lt;/td&gt;
&lt;td&gt;Static&lt;/td&gt;
&lt;td&gt;Release&lt;/td&gt;
&lt;td&gt;Success&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dynamic&lt;/td&gt;
&lt;td&gt;Release&lt;/td&gt;
&lt;td&gt;Static&lt;/td&gt;
&lt;td&gt;Debug&lt;/td&gt;
&lt;td&gt;Success&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;*Fails with both named and unnamed libraries with code:&lt;br&gt;
&lt;code&gt;runtime assert: runtime injected twice&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Testing environment&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Using simple framework importing, no cocoapods used. &lt;/li&gt;
&lt;li&gt;Statics set using &lt;code&gt;isStatic&lt;/code&gt; in gradle&lt;/li&gt;
&lt;li&gt;binaries.getFramework(“DEBUG”) or binaries.getFramework(“RELEASE”) for build type&lt;/li&gt;
&lt;li&gt;Kotlin 1.3.70&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So the only combination that fails is two debug static frameworks. Note that this will build successfully, however it will get a runtime assert of &lt;code&gt;runtime assert: runtime injected twice&lt;/code&gt; when running.&lt;/p&gt;
&lt;h2&gt;
  
  
  Are there any naming restrictions?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Answer&lt;/strong&gt;: Yes, but that’s true for &lt;em&gt;any&lt;/em&gt; kind of framework&lt;/p&gt;

&lt;p&gt;As you can imagine, you cannot have two frameworks with the same name. This would obviously confuse the compiler, even when using the &lt;code&gt;baseName&lt;/code&gt; variable in gradle. That being said you can have same-named classes in multiple libraries. If XCode gives you an &lt;code&gt;Ambiguous use of ___&lt;/code&gt; error, you can clarify by referencing it by its library. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;i.e. &lt;code&gt;framework1.MyClass&lt;/code&gt; vs &lt;code&gt;framework2.MyClass&lt;/code&gt;. &lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  How well do the modules talk to each other?
&lt;/h2&gt;

&lt;p&gt;This question is a little open ended, so to clarify we wanted to know what classes you can pass between your two Kotlin frameworks. The answer is actually more straightforward than you may think. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Answer&lt;/strong&gt;: Essentially, simple types and built-in classes like String work. Other types do not.&lt;/p&gt;

&lt;p&gt;The reason for this is because these built-in classes are using default Swift/Obj-C classes, so there’s no duplication. This does not work for custom classes because of how they’re defined in the framework. Custom classes described in one library are not identical, at a binary level, to that same class referenced in the second library. They are not interchangeable. Let’s look at an example. &lt;/p&gt;

&lt;p&gt;Example:&lt;br&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%2Fi%2F9fr62wr6xuwhr02uhj78.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%2Fi%2F9fr62wr6xuwhr02uhj78.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;(You have two Kotlin modules, &lt;code&gt;K1&lt;/code&gt; and &lt;code&gt;K2&lt;/code&gt;. Both create their own iOS framework. Both &lt;code&gt;K1&lt;/code&gt; and &lt;code&gt;K2&lt;/code&gt; have a dependency on the Kotlin library &lt;code&gt;BizLib&lt;/code&gt;. In &lt;code&gt;BizLib&lt;/code&gt; is a class called &lt;code&gt;Person&lt;/code&gt;. To iOS, they are different, and you cannot pass a &lt;code&gt;K1.Person&lt;/code&gt; into &lt;code&gt;K2&lt;/code&gt;)&lt;/p&gt;

&lt;p&gt;So when you try to pass in a class from one framework to another, you get this type of error:&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Cannot convert value of type K2Person to expected argument type K1Person&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;So you cannot pass custom objects into different frameworks, even if they’re referenced in the Kotlin code.&lt;/p&gt;

&lt;p&gt;As before, we did some testing of our own. We made some examples and saw what compiled and what gave errors. Here are our findings:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Element&lt;/th&gt;
&lt;th&gt;Working?&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Arrays&lt;/td&gt;
&lt;td&gt;Do Not Work&lt;/td&gt;
&lt;td&gt;Error: Cannot convert value of type 'Shared.KotlinArray' to expected argument type 'SecondLib.KotlinArray'&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lists&lt;/td&gt;
&lt;td&gt;Works&lt;/td&gt;
&lt;td&gt;Works unless the Element type is incompatible&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MutableLists&lt;/td&gt;
&lt;td&gt;Works&lt;/td&gt;
&lt;td&gt;Works unless the Element type is incompatible&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Maps&lt;/td&gt;
&lt;td&gt;Works&lt;/td&gt;
&lt;td&gt;Works unless the Key or Value type is incompatible&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MutableMaps&lt;/td&gt;
&lt;td&gt;Works&lt;/td&gt;
&lt;td&gt;Works unless the Key or Value type is incompatible&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;String&lt;/td&gt;
&lt;td&gt;Works&lt;/td&gt;
&lt;td&gt;If a string is passed into the Map it is converted to an NSString&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Boolean&lt;/td&gt;
&lt;td&gt;Works&lt;/td&gt;
&lt;td&gt;Can be compared successfully from both libraries&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Int&lt;/td&gt;
&lt;td&gt;Sometimes works&lt;/td&gt;
&lt;td&gt;For some reason, if an Int is passed into a list or map, it is converted to a “KotlinInt” and is not interchangeable. This is weird because just the int itself works fine.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Functions that return simple types (i.e. string, bool, int) work between frameworks&lt;/p&gt;

&lt;p&gt;We tested various classes you may use, and came up with some interesting results. As mentioned earlier built-in class seems to work successfully, as well as simple types. This is because there’s already existing swift classes, thus not making new and duplicate classes. The strange finding is that simple types in collections are in a boxed form, but those are classes specific to the framework. For example both classes appear as &lt;code&gt;Lib1.KotlinInt&lt;/code&gt; and &lt;code&gt;Lib2.KotlinInt&lt;/code&gt; when in a collection.&lt;/p&gt;

&lt;h2&gt;
  
  
  Recommendations
&lt;/h2&gt;

&lt;p&gt;While you can have multiple frameworks, because the same dependency between frameworks won't be compatible, having many Kotlin iOS frameworks won't really be practical. It'll make more sense to have very few or one Kotlin iOS framework that is composed of multiple features. However, in cases where your modules are very different, it may make sense to have multiple Kotlin iOS frameworks. The bottom line is, you can have multiple Kotlin frameworks, and while it is not as flexible as some had hoped, it's another configuration you can consider as you expand your Kotlin Multiplatform codebase.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;So those were our findings! While there is still a lot to explore with using multiple Kotlin frameworks we think this helps clear up a lot of things. We now know what does and doesn’t work at the moment when working with multiple frameworks, at least as of 1.3.70. With 1.4 being worked on right now some of these answers may change for the better. We'll be watching for these changes and looking into binary size considerations, debugging, and other issues soon.&lt;/p&gt;

</description>
      <category>kotlin</category>
      <category>kotlinmultiplatform</category>
      <category>android</category>
      <category>ios</category>
    </item>
    <item>
      <title>Kotlin Coroutines Cheat Sheet</title>
      <dc:creator>Kevin Schildhorn</dc:creator>
      <pubDate>Thu, 12 Dec 2019 15:54:40 +0000</pubDate>
      <link>https://forem.com/touchlab/kotlin-coroutines-cheat-sheet-5872</link>
      <guid>https://forem.com/touchlab/kotlin-coroutines-cheat-sheet-5872</guid>
      <description>&lt;p&gt;Recently I was refreshing myself on kotlin coroutines, as I had worked with them before but never fulled grasped all the ins and outs of coroutines, and I decided to make some notes while I was reading through the docs (&lt;a href="https://kotlinlang.org/docs/reference/coroutines/coroutines-guide.html"&gt;https://kotlinlang.org/docs/reference/coroutines/coroutines-guide.html&lt;/a&gt;). &lt;br&gt;
I figured they would be helpful to other kotlin developers. Note that these are quick notes I jotted down and isn't a fully grammatical tutorial-esque article.&lt;/p&gt;

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

&lt;p&gt;Coroutines are light-weight threads&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CoroutineScope&lt;/strong&gt; - Used to build coroutines, contains CoroutineContext&lt;br&gt;
    Scope Types:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GlobalScope&lt;/strong&gt; - Lifetime of the new coroutine is limited only by the lifetime of the whole application&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CoroutineScope&lt;/strong&gt; - Is destroyed after all launched children are completed&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MainScope&lt;/strong&gt; - Scope for UI applications and uses Dispatchers.Main&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;CoroutineContext&lt;/strong&gt; - A collection of various elements. Contains Job and CoroutineDispatcher&lt;br&gt;
&lt;strong&gt;CoroutineDispatcher&lt;/strong&gt; - Determines the thread(s) to use&lt;br&gt;
    Dispatcher types:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Unconfined&lt;/strong&gt; - I’m working in thread main (appropriate for coroutines which neither consume CPU time nor update any shared data (like UI) confined to a specific thread)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Default&lt;/strong&gt; -  I'm working in thread DefaultDispatcher-worker-1&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;newSingleThreadContext&lt;/strong&gt; - I’m working in thread MyOwnThread&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;main runBlocking&lt;/strong&gt; - I’m working in thread main&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;newSingleThreadContext()&lt;/strong&gt; - Creates new thread, can be used to switch between using withContext&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Launch
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Launch is a coroutine builder that returns a Job&lt;/li&gt;
&lt;li&gt;Launch inherits the context from the scope it’s launched from&lt;/li&gt;
&lt;li&gt;Customizing Launch:

&lt;ul&gt;
&lt;li&gt;Can pass variables between threads using &lt;code&gt;asContextElement&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Can name Coroutines using &lt;code&gt;CoroutineName&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Async
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Async is like launch, but returns a Deferred (i.e. future)&lt;/li&gt;
&lt;li&gt;Async &lt;code&gt;CoroutineStart.LAZY&lt;/code&gt; only starts when await is called&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Cancellation
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Jobs can be cancelled or call join, which works like pthread_join&lt;/li&gt;
&lt;li&gt;Coroutine cancellation is cooperative, meaning if the coroutine isn’t checking if it’s cancelled then it won’t be cancelled&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;SupervisorJob&lt;/code&gt; is a job where cancellation is only propagated downwards&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;withTimeout&lt;/code&gt; creates a timeout for the coroutines (throws &lt;code&gt;TimeoutCancellationException&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;When cancelled:

&lt;ul&gt;
&lt;li&gt;Job throws an exception which can be handled with finally or catch (&lt;code&gt;CancellationException&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;All child coroutines are cancelled as well&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Suspend
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Suspend can be used inside coroutines, but can also use other suspending functions&lt;/li&gt;
&lt;li&gt;If a coroutine builder is inside suspend function a CoroutineScope needs to be added as well&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Blocking
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;runBlocking&lt;/code&gt; runs a new coroutine and blocks the current thread, interruptible until it’s completion&lt;/li&gt;
&lt;li&gt;Delay is non blocking&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Flow
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Flow is an asynchronous stream of values . Uses &lt;code&gt;Emit&lt;/code&gt; and &lt;code&gt;Collect&lt;/code&gt; to send and collect data&lt;/li&gt;
&lt;li&gt;Flow runs in the context that is provided the collector&lt;/li&gt;
&lt;li&gt;The code inside a flow isn’t run until the flow is collected&lt;/li&gt;
&lt;li&gt;Flows can be cancelled with &lt;code&gt;withTimeoutOrNull&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Useful functions:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;transform&lt;/strong&gt; - customize data, such as emitting a header first&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;take&lt;/strong&gt; - only take a certain amount of the flow&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;flowOn&lt;/strong&gt; - change the context of preceding code&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;zip&lt;/strong&gt; - combine multiple flows&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;catch&lt;/strong&gt; - catch any exceptions of preceding code&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;onCompletion&lt;/strong&gt; - perform any final tasks after the flow is done&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Channel
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Similar to a blocking queue, a way to transfer values between coroutines&lt;/li&gt;
&lt;li&gt;Uses &lt;code&gt;send&lt;/code&gt; and &lt;code&gt;receive&lt;/code&gt; for normal values, &lt;code&gt;produce&lt;/code&gt; and &lt;code&gt;consume&lt;/code&gt; for streams&lt;/li&gt;
&lt;li&gt;Channels can be closed to indicate no more elements are coming&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Actor
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Actor is an entity that can receive a class of messages using sealed classes. Meaning a consumer that takes in sealed classes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let me know if I got anything wrong, or if I missed anything. Hope this helps!&lt;/p&gt;

</description>
      <category>kotlin</category>
      <category>coroutines</category>
      <category>android</category>
    </item>
  </channel>
</rss>
