<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Jigar Brahmbhatt</title>
    <description>The latest articles on DEV Community by Jigar Brahmbhatt (@shaktiman_droid).</description>
    <link>https://dev.to/shaktiman_droid</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%2F616824%2F71d43b46-bce1-4ef7-8f13-9ed75f3bf797.png</url>
      <title>DEV Community: Jigar Brahmbhatt</title>
      <link>https://dev.to/shaktiman_droid</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/shaktiman_droid"/>
    <language>en</language>
    <item>
      <title>Kotlin 1.9.20: Streamlining Source Sets in Multiplatform Project</title>
      <dc:creator>Jigar Brahmbhatt</dc:creator>
      <pubDate>Tue, 26 Mar 2024 13:57:17 +0000</pubDate>
      <link>https://dev.to/touchlab/kotlin-1920-streamlining-source-sets-in-multiplatform-project-27ak</link>
      <guid>https://dev.to/touchlab/kotlin-1920-streamlining-source-sets-in-multiplatform-project-27ak</guid>
      <description>&lt;p&gt;The release of Kotlin 1.9.20 marked a significant milestone for Kotlin Multiplatform enthusiasts, as this update moved Kotlin Multiplatform to Stable. Kotlin 1.9.20 introduces several notable &lt;a href="https://kotlinlang.org/docs/whatsnew1920.html"&gt;features and enhancements&lt;/a&gt;. Among these updates, the new source set hierarchy template by the Kotlin Gradle Plugin stands out to me for its ability to simplify configuration complexities. It was launched as experimental in Kotlin 1.8.20 and has been made default in Kotlin 1.9.20.&lt;/p&gt;

&lt;h2&gt;
  
  
  Simplified iOS Target Configuration
&lt;/h2&gt;

&lt;p&gt;In the common Kotlin Multiplatform project set-up, you would have seen specific source set setup for two or three iOS targets like for X64, Arm64.&lt;/p&gt;

&lt;p&gt;Instead of the common and confusing 'ios()' target, one must always declare specific targets like 'iosArm64()' as required. Once one of the iOS targets is declared, the default template would automatically create &lt;code&gt;iosMain&lt;/code&gt; source set. So no more &lt;code&gt;val iosMain by creating&lt;/code&gt; boilerplate required.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;tr&gt;
&lt;td&gt; Before 1.9.20 &lt;/td&gt; &lt;td&gt; After 1.9.20&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;

&lt;pre&gt;
kotlin {
    ios()
    iosSimulatorArm64()

    sourceSets {
        val commonMain by getting

        val iosMain by creating {
            dependsOn(commonMain)
        }

        val iosX64Main by getting {
            dependsOn(iosMain)
        }

        val iosArm64Main by getting {
            dependsOn(iosMain)
        }

        val iosSimulatorArm64Main by getting {
            dependsOn(iosMain)
        }
    }
}

&lt;/pre&gt;
&lt;/td&gt;
&lt;td&gt;

&lt;pre&gt;
kotlin {
    iosX64()
    iosArm64()
    iosSimulatorArm64()

    // The iosMain source set is now created automatically
    // You can directly use the reference now
    iosMain.dependencies {
        implementation("...")
    }
}
&lt;/pre&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;If you've an old KMP project and still have reference of &lt;code&gt;darwinMain&lt;/code&gt;, you can simply rename it to &lt;code&gt;appleMain&lt;/code&gt; now as that encapsulates all other apple targets.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://kotlinlang.org/docs/whatsnew1920.html#see-the-full-hierarchy-template"&gt;Checkout&lt;/a&gt; the full hierarchy template!&lt;/p&gt;

&lt;h2&gt;
  
  
  Streamlined Custom Source Sets
&lt;/h2&gt;

&lt;p&gt;Managing custom source sets in Kotlin Multiplatform projects is now more straightforward with Kotlin 1.9.20. The update simplifies the process of setting up dependencies between source sets, reducing the need for extensive boilerplate code and enhancing overall readability.&lt;/p&gt;

&lt;p&gt;For example, let's assume that you already have &lt;code&gt;kotlin/JS&lt;/code&gt; support with &lt;code&gt;jsMain/jsTest&lt;/code&gt; source sets. Now, you want to add &lt;code&gt;kotlinWasm&lt;/code&gt; support. Since &lt;code&gt;wasmJs&lt;/code&gt; and &lt;code&gt;kotlinJs&lt;/code&gt; can share a common source, you want to introduce a new &lt;code&gt;jsAndWasmJsMain&lt;/code&gt; parent source set for common code sharing between them. Instead of using &lt;code&gt;by creating&lt;/code&gt;, you can declare new custom source set inside &lt;code&gt;applyDefaultHierarchyTemplate {}&lt;/code&gt;.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;tr&gt;
&lt;td&gt; Before 1.9.20 &lt;/td&gt; &lt;td&gt; After 1.9.20&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;pre&gt;
kotlin {
    js()
    wasmJs()

    sourceSets {
        val commonMain by getting
        val commonTest by getting

        val jsAndWasmJsMain by creating {
            dependsOn(commonMain)
            getByName("jsMain").dependsOn(this)
            getByName("wasmMain").dependsOn(this)
        }

        val jsAndWasmJsTest by creating {
            dependsOn(commonTest)
            getByName("jsTest").dependsOn(this)
            getByName("wasmTest").dependsOn(this)
        }
    }
}
&lt;/pre&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;pre&gt;
kotlin {
    js()
    wasmJs()

    // This is an experimental API, so opt-in is required
    @OptIn(ExperimentalKotlinGradlePluginApi::class)
    applyDefaultHierarchyTemplate {
        // create a new group that
        // depends on `common`
        common {
            // Define group name without
            // `Main` as suffix
            group("jsAndWasmJs") {
                // Provide which targets would
                // be part of this group
                withJs()
                withWasm()
            }
        }
    }

    // Now you will already have `jsAndWasmJsMain`
}
&lt;/pre&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Manually apply the template
&lt;/h3&gt;

&lt;p&gt;Note that if you have explict &lt;code&gt;by creating&lt;/code&gt; usage for custom source set then defauly template hierarchy would not apply automatically and you would see warning about the same.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;w: The Default Kotlin Hierarchy Template was not applied to 'project ':XXX'':
Explicit .dependsOn() edges were configured for the following source sets:
[XXX]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In order to get the default hierachy, you would have to call &lt;code&gt;applyDefaultHierarchyTemplate()&lt;/code&gt; manually once you declare all the targets.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Also, checkout Opting Out of Default Templates&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Enhanced Code Completion Support
&lt;/h2&gt;

&lt;p&gt;The updated source set hierarchy template enhances code completion support in the IDE. Developers can now utilize predefined source sets directly, eliminating the need for repetitive declarations and enhancing overall development efficiency.&lt;/p&gt;

&lt;p&gt;So even in simple cases, you can still clean up your source set declarations, and make them more readable.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;tr&gt;
&lt;td&gt; Default before 1.9.20 &lt;/td&gt; &lt;td&gt; With 1.9.20 &lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;pre&gt;
kotlin {
    // ...
    sourceSets {
        val commonMain by getting {
            dependencies {
                // ...
            }
        }
        val androidMain by getting {
            dependencies {
                // ...
            }
        }
        val iosMain by creating {
            dependencies {
                // ...
            }
        }
    }
}
&lt;/pre&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;pre&gt;
kotlin {
    // ...
    sourceSets {
        commonMain.dependencies {
            // ...
        }
        androidMain.dependencies {
            // ..
        }
        iosMain.dependencies {
            // ...
        }
    }
}
&lt;/pre&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Android instrumented test changes
&lt;/h2&gt;

&lt;p&gt;With introduction of the new Android source set layout (now default starting Kotlin 1.9.0), the &lt;code&gt;androidInstrumentedTest&lt;/code&gt; source set doesn't automatically depend on &lt;code&gt;commonTest&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In order to have that dependency set-up, with 1.9.0, you would need to explicitly set the &lt;code&gt;dependsOn&lt;/code&gt; relationship between them, and with 1.9.20, you now just need to set the correct &lt;code&gt;sourceSetTree&lt;/code&gt; property inside android target.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;tr&gt;
&lt;td&gt; With 1.9.0 &lt;/td&gt; &lt;td&gt; With 1.9.20&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;pre&gt;
kotlin {
    // ...
    sourceSets {
        val commonTest by getting
        val androidInstrumentedTest by getting {
            dependsOn(commonTest)
        }
    }
}
&lt;/pre&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;pre&gt;
kotlin {
    androidTarget {
        // This is an experimental API, so opt-in is required
        @OptIn(ExperimentalKotlinGradlePluginApi::class)
        instrumentedTestVariant {
            // This makes instrumented tests depend on commonTest source
            sourceSetTree.set(KotlinSourceSetTree.test)
        }
    }
}
&lt;/pre&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Opting Out of Default Templates
&lt;/h2&gt;

&lt;p&gt;For projects with existing complex source set hierarchies, Kotlin 1.9.20 offers the flexibility to opt-out of the default template. By setting &lt;code&gt;kotlin.mpp.applyDefaultHierarchyTemplate=false&lt;/code&gt; in the &lt;code&gt;gradle.properties&lt;/code&gt;, developers can retain their current configurations.&lt;/p&gt;

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

&lt;p&gt;As you transition to Kotlin 1.9.20, consider how these improvements can optimize your Gradle configurations and enhance your multiplatform development workflow.&lt;/p&gt;

&lt;p&gt;We recently updated most of our OSS projects to use Kotlin 1.9.22, leveraging the benefits of these enhancements. We hope you find the transition as beneficial as we have.&lt;/p&gt;




&lt;p&gt;Share your experiences and questions with us on #touchlab-tools channel on Kotlin Slack. Also, you can reach out to me at &lt;a class="mentioned-user" href="https://dev.to/shaktiman_droid"&gt;@shaktiman_droid&lt;/a&gt; on Twitter(X) or LinkedIn. Happy coding!&lt;/p&gt;

</description>
      <category>kotlin</category>
      <category>kotlinmultiplatform</category>
      <category>gradle</category>
      <category>kmp</category>
    </item>
    <item>
      <title>Kermit Now Supports WASM</title>
      <dc:creator>Jigar Brahmbhatt</dc:creator>
      <pubDate>Mon, 30 Oct 2023 19:09:32 +0000</pubDate>
      <link>https://dev.to/touchlab/kermit-now-supports-wasm-356p</link>
      <guid>https://dev.to/touchlab/kermit-now-supports-wasm-356p</guid>
      <description>&lt;h2&gt;
  
  
  What is Kotlin/Wasm?
&lt;/h2&gt;

&lt;p&gt;For Kotlin Multiplatform developers, the conversation has shifted to WebAssembly — a notable addition to the Kotlin Multiplatform (KMP) ecosystem. &lt;a href="(https://en.wikipedia.org/wiki/WebAssembly)"&gt;WebAssembly&lt;/a&gt;, often abbreviated as WASM, is a binary instruction format tailored for web browsers. Its main goal is to execute high-performance code at speeds close to native applications. WebAssembly enjoys broad support across modern browsers, boasting approximately 96% coverage according to &lt;a href="https://caniuse.com/wasm"&gt;caniuse&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;While it's not meant to be directly written, there's a fascinating aspect called &lt;code&gt;WebAssembly Text Format (.wat)&lt;/code&gt;. This format allows developers to represent code in a readable text format. For those curious minds, delve deeper into this format by reading &lt;a href="https://developer.mozilla.org/en-US/docs/WebAssembly/Understanding_the_text_format"&gt;Mozilla's guide&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;While WebAssembly isn't a newcomer, ongoing developments, such as the anticipated &lt;a href="https://github.com/WebAssembly/gc"&gt;WebAssembly Garbage Collection (WASM GC)&lt;/a&gt;, make it an evolving technology. Kotlin, recognizing the potential of WebAssembly, has embraced it with the Kotlin/WASM compiler. The Kotlin team is actively ensuring they stay ahead of the curve with the Kotlin/WASM compiler, already incorporating support for features like WASM GC. What makes Kotlin/WASM noteworthy is its ability to allow developers to write code in their preferred language and seamlessly compile it into a WebAssembly binary.&lt;/p&gt;

&lt;h2&gt;
  
  
  Kermit Steps into the Wasm Arena
&lt;/h2&gt;

&lt;p&gt;Given Kermit's popularity in the Kotlin Multiplatform (KMP) realm, the community asked for Wasm support. It was exciting to gain firsthand experience with WASM for integration into Kermit.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step #1 - Adding the Wasm Target
&lt;/h3&gt;

&lt;p&gt;The initial step seemed straightforward: add the &lt;code&gt;wasm&lt;/code&gt; target in our build scripts. However, reality hit when we discovered that some of Kermit's dependencies lacked support for Wasm. This underscored a critical lesson for library developers: &lt;strong&gt;ensure upstream libraries are compatible with all KMP targets your project aims to include&lt;/strong&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;import&lt;/span&gt; &lt;span class="nn"&gt;org.jetbrains.kotlin.gradle.targets.js.dsl.ExperimentalWasmDsl&lt;/span&gt;

&lt;span class="nf"&gt;kotlin&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
    &lt;span class="nd"&gt;@OptIn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ExperimentalWasmDsl&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;wasm&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;binaries&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;executable&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;blockquote&gt;
&lt;p&gt;You can also add &lt;code&gt;nodejs&lt;/code&gt; and &lt;code&gt;d8&lt;/code&gt; environment support inside the &lt;code&gt;wasm&lt;/code&gt; target, but exploration around that is out of scope for this post.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Step #2 - Common Source Set
&lt;/h3&gt;

&lt;p&gt;Understanding the possibility for &lt;a href="(https://kotlinlang.org/docs/wasm-js-interop.html)"&gt;interoperability&lt;/a&gt; between Kotlin/JS and Kotlin/Wasm, we defined a common source set shared between &lt;code&gt;js&lt;/code&gt; and &lt;code&gt;wasm&lt;/code&gt;. This approach ensures code reusability between the two targets.&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;kotlin&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;sourceSets&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;jsAndWasmMain&lt;/span&gt; &lt;span class="k"&gt;by&lt;/span&gt; &lt;span class="nf"&gt;creating&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nf"&gt;dependsOn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;commonMain&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;jsMain&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dependsOn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;wasmMain&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dependsOn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&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;jsAndWasmTest&lt;/span&gt; &lt;span class="k"&gt;by&lt;/span&gt; &lt;span class="nf"&gt;creating&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nf"&gt;dependsOn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;commonTest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;jsTest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dependsOn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;wasmTest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dependsOn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step #3 - Code Migration
&lt;/h3&gt;

&lt;p&gt;Migrating code from &lt;code&gt;jsMain&lt;/code&gt; to &lt;code&gt;jsAndWasmMain&lt;/code&gt; wasn't a simple copy-paste endeavor. Here, we encountered two challenges:&lt;/p&gt;

&lt;h4&gt;
  
  
  Problem #1: Console API Unavailability in Wasm
&lt;/h4&gt;

&lt;p&gt;The first hurdle we encountered was the absence of the &lt;a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.js/-console/"&gt;console API&lt;/a&gt; in Wasm. In Kermit, we had the &lt;a href="https://github.com/touchlab/Kermit/blob/1fca73033063b3a52faa14b886d9e5422d2fca49/kermit-core/src/jsMain/kotlin/co/touchlab/kermit/ConsoleWriter.kt#L52"&gt;ConsoleActual&lt;/a&gt; object in JS, utilizing the console API to log data to the browser's console. To address this, we took the following steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Converted the ConsoleActual object to an expect object, establishing a mandatory object for JS and Wasm:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="n"&gt;expect&lt;/span&gt; &lt;span class="kd"&gt;object&lt;/span&gt; &lt;span class="nc"&gt;ConsoleActual&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;ConsoleIntf&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Moved the existing object with console calls to &lt;code&gt;jsMain&lt;/code&gt; and created a new one for &lt;code&gt;wasmMain&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  Problem #2: Wasm-JS Interop Limitations
&lt;/h4&gt;

&lt;p&gt;To call console from Wasm, we needed to invoke JavaScript from Wasm. While the usual &lt;code&gt;js()&lt;/code&gt; for inline JavaScript works for JS, Wasm required a different approach. The &lt;a href="https://kotlinlang.org/docs/wasm-js-interop.html#inline-javascript"&gt;@JSFun&lt;/a&gt; annotation proved useful, allowing us to execute JavaScript functions from Wasm. However, we encountered a problem with use of &lt;code&gt;Any?&lt;/code&gt; in the interface methods:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Wasm.ConsoleActual.kt&lt;/strong&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="nd"&gt;@JsFun&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"(output) =&amp;gt; console.error(output)"&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;consoleError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;vararg&lt;/span&gt; &lt;span class="n"&gt;output&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="k"&gt;internal&lt;/span&gt; &lt;span class="n"&gt;actual&lt;/span&gt; &lt;span class="kd"&gt;object&lt;/span&gt; &lt;span class="nc"&gt;ConsoleActual&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;ConsoleIntf&lt;/span&gt; &lt;span class="p"&gt;{&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;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;vararg&lt;/span&gt; &lt;span class="n"&gt;output&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="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;consoleError&lt;/span&gt;&lt;span class="p"&gt;(*&lt;/span&gt;&lt;span class="n"&gt;output&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;Unfortunately, an inline error surfaced:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Type Any? cannot be used in an external function parameter. Only external, primitive, string, and function types are supported in Kotlin/Wasm JS interop.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To circumvent this limitation, we opted for a design change, using &lt;code&gt;String&lt;/code&gt; instead of &lt;code&gt;Any?&lt;/code&gt; for both JS and Wasm, as they share the same interface:&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;internal&lt;/span&gt; &lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;ConsoleIntf&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;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;o&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="c1"&gt;// other methods...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This adjustment allowed smooth interaction between Wasm and JS, ensuring compatibility with the restricted set of supported APIs in Kotlin/Wasm.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step #4 - Integration
&lt;/h3&gt;

&lt;p&gt;Drawing inspiration from Kotlin's &lt;a href="https://github.com/Kotlin/kotlin-wasm-examples/tree/main/browser-example"&gt;wasm-browser-sample&lt;/a&gt;, we created the &lt;a href="https://github.com/touchlab/Kermit/tree/main/samples/sample/wasm-browser"&gt;wasm-browser&lt;/a&gt; sample app. It mirrors the module structure of the app-browser app in our Kotlin/JS sample.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lessons Learned
&lt;/h2&gt;

&lt;p&gt;Our foray into Kotlin/Wasm integration was not without challenges. Key takeaways include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Interoperability with JS is a work in progress, requiring careful consideration when sharing code between the two.&lt;/li&gt;
&lt;li&gt;Loading generated &lt;code&gt;.wasm&lt;/code&gt; files directly in web apps may pose challenges, especially around types. We couldn't get this to work in the short time we had.&lt;/li&gt;
&lt;li&gt;Debugging Wasm-related issues can be tricky; Our tests failure output wasn't easy to understand. Kotlin Slack proved to be an invaluable resource for troubleshooting. We learned that output with &lt;code&gt;--info&lt;/code&gt; flag shows the actual node command that's being executed. Calling that command directly showed better errors and we could fix the code then.&lt;/li&gt;
&lt;li&gt;Compatibility concerns arise regarding the version of &lt;code&gt;Node.js&lt;/code&gt;; Note that versions 1.9.20 onwards would require Node version and Browsers that support Wasm GC.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Feel free to explore the latest version of Kermit and dive into the exciting world of Kotlin/Wasm. Stay tuned for future updates and enhancements! Share your experiences and questions with us on &lt;a href="https://kotlinlang.slack.com/archives/CTJB58X7X"&gt;#touchlab-tools&lt;/a&gt; channel on Kotlin Slack. Also, you can reach out to me at &lt;a href="https://twitter.com/shaktiman_droid"&gt;@shaktiman_droid&lt;/a&gt; on Twitter(X) or &lt;a href="https://www.linkedin.com/in/shaktiman-droid/"&gt;LinkedIn&lt;/a&gt;. Happy coding!&lt;/p&gt;

</description>
      <category>kotlinmultiplatform</category>
      <category>kotlin</category>
      <category>webassembly</category>
      <category>logging</category>
    </item>
    <item>
      <title>Logger.i() Not Your Style? Customize Kermit Logger!</title>
      <dc:creator>Jigar Brahmbhatt</dc:creator>
      <pubDate>Tue, 24 Oct 2023 02:19:28 +0000</pubDate>
      <link>https://dev.to/touchlab/loggeri-not-your-style-customize-kermit-logger-4fik</link>
      <guid>https://dev.to/touchlab/loggeri-not-your-style-customize-kermit-logger-4fik</guid>
      <description>&lt;p&gt;In the realm of Kotlin Multiplatform logging, the Kermit library stands as a trusted companion for developers. However, some find its conventional &lt;code&gt;logger.i&lt;/code&gt; syntax less appealing. What if you could improve your logging experience with a touch of personalization? Enter the world of custom logger, where &lt;code&gt;logger.info&lt;/code&gt; becomes a reality.&lt;/p&gt;

&lt;h2&gt;
  
  
  Diving into Custom Logging
&lt;/h2&gt;

&lt;p&gt;The journey begins with a glance at a custom code snippet that transforms the Kermit logging experience. This code introduces a custom &lt;code&gt;Logger&lt;/code&gt; class adorned with methods like &lt;code&gt;info&lt;/code&gt;, and &lt;code&gt;error&lt;/code&gt;—a testament to the flexibility &lt;code&gt;Kermit&lt;/code&gt; provides for tailoring logging to your liking.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note that example code below is a very toned down version of full Logger API. It helps with keeping the code minimal while still delivering the idea behind it.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&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;co.touchlab.kermit.BaseLogger&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;co.touchlab.kermit.LoggerConfig&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;co.touchlab.kermit.Severity&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;co.touchlab.kermit.mutableLoggerConfigInit&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;co.touchlab.kermit.platformLogWriter&lt;/span&gt;

&lt;span class="k"&gt;open&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Logger&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;LoggerConfig&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;open&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;tag&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="s"&gt;"MyDefaultTag"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;BaseLogger&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="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;inline&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;throwable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Throwable&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tag&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;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tag&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;span class="p"&gt;-&amp;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="nf"&gt;logBlock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Info&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;throwable&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;span class="k"&gt;inline&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;throwable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Throwable&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tag&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;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tag&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;span class="p"&gt;-&amp;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="nf"&gt;logBlock&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;tag&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;throwable&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;span class="k"&gt;inline&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;messageString&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;throwable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Throwable&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tag&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;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Info&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;throwable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;messageString&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;inline&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;messageString&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;throwable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Throwable&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tag&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;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;log&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;tag&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;throwable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;messageString&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;companion&lt;/span&gt; &lt;span class="k"&gt;object&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Logger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;mutableLoggerConfigInit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;listOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;platformLogWriter&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 instead of calling Kermit's API, you can call your own methods.&lt;/p&gt;

&lt;p&gt;Call&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;Logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="p"&gt;.)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;rather than&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;Logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;i&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="p"&gt;.)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Kermit-core
&lt;/h3&gt;

&lt;p&gt;This flexiblity comes from the modularized kermit components. If you prefer to have &lt;code&gt;custom&lt;/code&gt; logger then you need to only depends on &lt;code&gt;kermit-core&lt;/code&gt; instead of &lt;code&gt;kermit&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="n"&gt;implementation&lt;/span&gt; &lt;span class="s"&gt;"co.touchlab:kermit-core:{{KERMIT_VERSION}}"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Beyond syntax preferences, custom logger methods may include more flexibility matching your application's unique needs.&lt;/p&gt;

&lt;p&gt;Embrace the power of custom logging with &lt;code&gt;Kermit&lt;/code&gt;, and let your logs echo your coding identity. Happy logging!&lt;/p&gt;




&lt;p&gt;Let me know in the comments if you have questions. Also, you can reach out to me at &lt;a href="https://twitter.com/shaktiman_droid"&gt;@shaktiman_droid&lt;/a&gt; on Twitter(X), &lt;a href="https://www.linkedin.com/in/shaktiman-droid/"&gt;LinkedIn&lt;/a&gt; or &lt;a href="https://kotlinlang.slack.com/archives/CTJB58X7X"&gt;#touchlab-tools&lt;/a&gt; channel on Kotlin Slack. And if you find all this interesting, maybe you'd like to &lt;a href="https://touchlab.co/contact-us/"&gt;work with&lt;/a&gt; or &lt;a href="https://touchlab.co/careers-3/"&gt;work at&lt;/a&gt; Touchlab.&lt;/p&gt;

</description>
      <category>kotlinmultiplatform</category>
      <category>kotlin</category>
      <category>logging</category>
      <category>mobile</category>
    </item>
    <item>
      <title>Optimizing Gradle Builds in Multi-module Projects</title>
      <dc:creator>Jigar Brahmbhatt</dc:creator>
      <pubDate>Mon, 16 Oct 2023 20:31:31 +0000</pubDate>
      <link>https://dev.to/touchlab/optimizing-gradle-builds-in-multi-module-projects-3ijp</link>
      <guid>https://dev.to/touchlab/optimizing-gradle-builds-in-multi-module-projects-3ijp</guid>
      <description>&lt;p&gt;You're not alone if you've also struggled with sluggish Gradle builds in a multi-module project. Recently, we undertook the challenge of significantly reducing build times for a client with over 100 Kotlin Multiplatform modules, and achieved a more than 50% boost in speed across platforms. In this post, we'll walk you through the steps we took. We hope it proves valuable to fellow developers facing similar challenges.&lt;/p&gt;

&lt;h2&gt;
  
  
  Benchmark Your Builds
&lt;/h2&gt;

&lt;p&gt;Before diving into optimizations, it's crucial to understand the baseline.  Gather build times from all team members, ensuring the Gradle build cache is disabled. Use handy &lt;code&gt;--no-build-cache&lt;/code&gt; option to any Gradle task command to run without build cache. Unsurprisingly, in our case, the Intel-based mac machines were really slow compared to Apple chip ones and we didn't have anyone with a Windows machine. This information is key for later comparison and improvement assessment.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: One thing we realized that, some optimizations below might be more applicable to legacy or tech-debt-laden projects.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Tools for Insight
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Gradle Build Scan
&lt;/h3&gt;

&lt;p&gt;Utilize the power of Gradle &lt;a href="(https://docs.gradle.org/current/userguide/inspect.html#what_is_a_build_scan)"&gt;Build Scan&lt;/a&gt; to delve into the details of your builds. In my opinion, it's not just a diagnostic tool; it's a learning tool.&lt;/p&gt;

&lt;p&gt;In our case, we uncovered insights into the sluggishness of &lt;code&gt;ios link&lt;/code&gt; tasks in the Kotlin Multiplatform setup. Comparing build scans on different CI machines helped identify the fastest configuration for a machine to use on our CI. Enabling parallel builds and analyzing the results through build scan reports further validated our improvements.&lt;/p&gt;

&lt;h3&gt;
  
  
  Android Studio Build Analyser
&lt;/h3&gt;

&lt;p&gt;Android Studio provides a &lt;a href="https://developer.android.com/build/build-analyzer"&gt;built-in analyser tool&lt;/a&gt;. It inspects build performance and provides warning around potential issues.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Disable Jetifier&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Using this, we noticed the lingering &lt;code&gt;android.enableJetifier=true&lt;/code&gt; usage due to outdated &lt;code&gt;Picasso&lt;/code&gt; library that didn't use &lt;code&gt;AndroidX&lt;/code&gt; libraries. That might have hindered build speed too. Addressing this involved updating &lt;code&gt;Picasso&lt;/code&gt; and removing the &lt;code&gt;jetifier&lt;/code&gt; flag from &lt;code&gt;gradle.properties&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;MultiDex&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Through the Build Analyser, we also found unnecessary use of &lt;code&gt;multiDexEnabled&lt;/code&gt; in few modules along with some old Gradle configurations. &lt;code&gt;MultiDex&lt;/code&gt; was unnecessary with &lt;code&gt;minSdkVersion&lt;/code&gt; as 21.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dependency Visibility
&lt;/h2&gt;

&lt;p&gt;We observed a significant number of modules relying on other modules via &lt;code&gt;api&lt;/code&gt; dependency. The problem with having &lt;code&gt;api&lt;/code&gt; dependencies is that Gradle would &lt;code&gt;recompile&lt;/code&gt; them when implementation details change. It's because they appear on &lt;code&gt;compile&lt;/code&gt; classpaths. This can have significant &lt;code&gt;ripple&lt;/code&gt; of recompilations in a multi-module setup and affect the build times.&lt;/p&gt;

&lt;p&gt;To mitigate this, we shifted to using &lt;code&gt;implementation&lt;/code&gt; for most dependencies, reserving &lt;code&gt;api&lt;/code&gt; only for those exported as part of &lt;code&gt;XCFramework&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enable Gradle Parallel Execution
&lt;/h2&gt;

&lt;p&gt;In a multi-subproject setup, Gradle may benefit greatly from parallel execution. With the shift from &lt;code&gt;api&lt;/code&gt; to &lt;code&gt;implementation&lt;/code&gt;, &lt;a href="https://docs.gradle.org/current/userguide/performance.html#parallel_execution"&gt;enabling parallel execution&lt;/a&gt; further slashed build times.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mindful Use of External Gradle Plugins
&lt;/h2&gt;

&lt;p&gt;Some Gradle plugins can be the culprits behind slower builds. Our project had a couple of Gradle plugins like that. They used to execute on every sync, every task and run through all the modules in the project.&lt;/p&gt;

&lt;p&gt;Scrutinize third-party plugins before integration, especially in a multi-module setup. Avoid unnecessary global application using &lt;code&gt;subproject&lt;/code&gt; or &lt;code&gt;allprojects&lt;/code&gt; blocks; apply plugins only where needed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Unnecessary KMP targets
&lt;/h2&gt;

&lt;p&gt;When managing Kotlin Multiplatform (KMP) projects, it's crucial to evaluate the necessity of added KMP targets. In our experience, we found that inadvertently adding targets that aren't required for the project can introduce inefficiencies.&lt;/p&gt;

&lt;p&gt;For instance, we had inherited some modules with added &lt;code&gt;JVM&lt;/code&gt; targets for certain KMP modules from older test modules. Over time, more modules were introduced, and the JVM target persisted without any actual use. This led to unnecessary JVM-related build and test tasks executing during the build phase.&lt;/p&gt;

&lt;p&gt;To address this, we recommend reviewing your KMP setup and removing any targets that don't contribute to the project's functionality. This not only streamlines the build process but also reduces unnecessary overhead, especially in a large multi-module project like ours.&lt;/p&gt;

&lt;h2&gt;
  
  
  Build only required XCFramework
&lt;/h2&gt;

&lt;p&gt;In Kotlin Multiplatform (KMP) projects with iOS components, the iOS build task can be a significant contributor to extended build times. Often, KMP project script files include configurations for multiple iOS architectures using &lt;code&gt;iOSX64()&lt;/code&gt;, &lt;code&gt;iosArm64()&lt;/code&gt;, and &lt;code&gt;iosSimulatorArm64()&lt;/code&gt; targets, or the shorthand &lt;code&gt;ios()&lt;/code&gt; that enables both &lt;code&gt;iosArm64&lt;/code&gt; and &lt;code&gt;iOSX64&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;However, it's essential to optimize this setup, especially considering that building for unnecessary architectures can consume substantial time and resources. For example, even if your local development machine has an &lt;code&gt;ARM&lt;/code&gt; architecture, building the &lt;code&gt;X64&lt;/code&gt; framework unnecessarily adds to build times, and vice versa for other architectures.&lt;/p&gt;

&lt;p&gt;Generally speaking, if things work on one architecture, then it's high likely that it would work on other architectures too. So it's fine if you at least keep building only one architecture locally. One approach is to introduce a boolean flag in your gradle.properties file, creating custom logic to enable a specific iOS target.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Are you on a team where not everybody calling the Kotlin code wants or needs to build it locally? We've built &lt;a href="https://github.com/touchlab/KMMBridge"&gt;KMMBridge&lt;/a&gt;, a tool to help streamline the iOS dev flow in KMP.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Be Careful with Custom Gradle Tasks
&lt;/h2&gt;

&lt;p&gt;While creating custom Gradle tasks might seem like a convenient way to handle common requirements, it's crucial to exercise caution, especially in a multi-module setup. In our experience, some custom tasks had unintended consequences on build times.&lt;/p&gt;

&lt;p&gt;For instance, we had custom tasks responsible for copying resources to another folder, but they ran across all modules, resulting in a noticeable slowdown during the build. To address this issue, we reconsidered our approach and opted for a different strategy to achieve the desired outcome without compromising build efficiency.&lt;/p&gt;

&lt;p&gt;The key takeaway is to ensure that custom tasks are appropriately configured to avoid unnecessary repetition across modules or tasks&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep build tools up-to-date
&lt;/h2&gt;

&lt;p&gt;Always stay current with Android Studio, Android Gradle Plugin, and Gradle itself. Each iteration brings improvements, potentially enhancing build speed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AGP 8.+&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Upgrade to Android Gradle Plugin 8.0.0 for default behaviors that optimize builds (i.e. &lt;a href="https://developer.android.com/build/optimize-your-build#use-non-transitive-r-classes"&gt;non-transitive R classes&lt;/a&gt;), especially for apps with multiple modules.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Configuration Cache&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Newer build tools have better support for configuration cache. It seems like a very promising feature that can drastically improve build speeds in certain cases.&lt;/p&gt;

&lt;p&gt;While configuration cache is a promising feature for faster builds, ensure compatibility with all your tools. As of Kotlin 1.9.10, the Kotlin Multiplatform plugin still lacks full support, limiting its effectiveness.&lt;/p&gt;

&lt;p&gt;Kotlin 1.9.20 is supposed to being &lt;a href="https://kotlinlang.org/docs/whatsnew-eap.html#full-support-for-the-gradle-configuration-cache-in-kotlin-multiplatform"&gt;full support&lt;/a&gt; for configuration cache. We're pretty pumped about it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Additional Considerations
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Ensure Gradle &lt;a href="https://docs.gradle.org/current/userguide/build_cache.html"&gt;Build Cache&lt;/a&gt; is enabled.&lt;/li&gt;
&lt;li&gt;Amount of code can be overwhelming in Gradle files with multi-module setup. Simplify Gradle scripts using the &lt;a href="https://docs.gradle.org/current/userguide/custom_plugins.html#sec:precompiled_plugins"&gt;Gradle convention plugin&lt;/a&gt; to minimize duplicate configuration.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;By following these steps, you may turbocharge your Gradle builds, regardless of the project size or complexity. Happy coding! Let me know in the comments if you have questions. Also, you can reach out to me at &lt;a href="https://twitter.com/shaktiman_droid"&gt;@shaktiman_droid&lt;/a&gt; on Twitter, &lt;a href="https://www.linkedin.com/in/shaktiman-droid/"&gt;LinkedIn&lt;/a&gt; or &lt;a href="https://slack.kotlinlang.org/"&gt;Kotlin Slack&lt;/a&gt;. And if you find all this interesting, maybe you'd like to &lt;a href="https://touchlab.co/contact-us/"&gt;work with&lt;/a&gt; or &lt;a href="https://touchlab.co/careers-3/"&gt;work at&lt;/a&gt; Touchlab.&lt;/p&gt;

</description>
      <category>gradle</category>
      <category>kotlin</category>
      <category>kotlinmultiplatform</category>
      <category>mobile</category>
    </item>
    <item>
      <title>Jetpack Compose: A use case for view interop migration strategy</title>
      <dc:creator>Jigar Brahmbhatt</dc:creator>
      <pubDate>Mon, 13 Feb 2023 17:59:33 +0000</pubDate>
      <link>https://dev.to/touchlab/jetpack-compose-a-use-case-for-view-interop-migration-strategy-4dba</link>
      <guid>https://dev.to/touchlab/jetpack-compose-a-use-case-for-view-interop-migration-strategy-4dba</guid>
      <description>&lt;h2&gt;
  
  
  Approach
&lt;/h2&gt;

&lt;p&gt;Recently, we had an opportunity to redesign the sample app for an SDK we're developing. We were only redesigning one screen (home page), so we decided to introduce &lt;code&gt;Jetpack Compose&lt;/code&gt; in the app.&lt;/p&gt;

&lt;p&gt;Based on what we read, most migration or view interop posts are about keeping &lt;code&gt;Fragments&lt;/code&gt; and &lt;code&gt;fragment navigation&lt;/code&gt; as they are and moving the content of the fragments into &lt;code&gt;ComposeView&lt;/code&gt;. In fact, &lt;a href="https://developer.android.com/jetpack/compose/interop/migration-strategy" rel="noopener noreferrer"&gt;official docs also mention that strategy.&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;For that, we would still have to touch other fragments in the app rather than just the home page. So it felt like more work than what we intended to do. On top of that, the sample app extensively uses &lt;code&gt;PreferenceFragmentCompat&lt;/code&gt; for various setting screens. To move its content to &lt;code&gt;ComposeView&lt;/code&gt;, we would have to re-create a kind of preferences screen from scratch because compose doesn't have a straight replacement for &lt;code&gt;PreferenceFragment&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;So we decided to go with the &lt;a href="https://developer.android.com/jetpack/compose/interop/interop-apis#fragments-in-compose" rel="noopener noreferrer"&gt;Fragments in Compose&lt;/a&gt; approach. Along with that, we also moved our navigation using &lt;a href="https://developer.android.com/jetpack/compose/navigation" rel="noopener noreferrer"&gt;compose navigation component&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fragments in Compose
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Layout
&lt;/h3&gt;

&lt;p&gt;We created new layout files for each fragment with &lt;a href="https://developer.android.com/reference/androidx/fragment/app/FragmentContainerView" rel="noopener noreferrer"&gt;FragmentContainerView&lt;/a&gt; in each.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?xml version="1.0" encoding="utf-8"?&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;androidx.fragment.app.FragmentContainerView&lt;/span&gt; &lt;span class="na"&gt;xmlns:android=&lt;/span&gt;&lt;span class="s"&gt;"http://schemas.android.com/apk/res/android"&lt;/span&gt;
    &lt;span class="na"&gt;android:id=&lt;/span&gt;&lt;span class="s"&gt;"@+id/fragment_container_view_events"&lt;/span&gt;
    &lt;span class="na"&gt;android:name=&lt;/span&gt;&lt;span class="s"&gt;"com.example.fragment.analytics.AnalyticsEventsFragment"&lt;/span&gt;
    &lt;span class="na"&gt;android:layout_width=&lt;/span&gt;&lt;span class="s"&gt;"match_parent"&lt;/span&gt;
    &lt;span class="na"&gt;android:layout_height=&lt;/span&gt;&lt;span class="s"&gt;"match_parent"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;/androidx.fragment.app.FragmentContainerView&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Composable
&lt;/h3&gt;

&lt;p&gt;We wrote a generic &lt;code&gt;FragmentHolder&lt;/code&gt; composable with &lt;a href="https://developer.android.com/reference/kotlin/androidx/compose/ui/viewinterop/package-summary#AndroidViewBinding(kotlin.Function3,androidx.compose.ui.Modifier,kotlin.Function1)" rel="noopener noreferrer"&gt;AndroidViewBinding&lt;/a&gt;. It composes an Android layout resource. The layout files we created above would have their &lt;code&gt;Binding&lt;/code&gt; class generated that we can inflate and pass as &lt;code&gt;BindingFactory&lt;/code&gt; in the &lt;code&gt;AndroidViewBinding&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="nd"&gt;@Composable&lt;/span&gt;
&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;T&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;ViewBinding&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;FragmentHolderScreen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;topBarTitle&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;androidViewBindingFactory&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;inflater&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;LayoutInflater&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;ViewGroup&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;attachToParent&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="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;T&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;onBackPress&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="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
    &lt;span class="n"&gt;androidViewBindingUpdate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;T&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="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;Scaffold&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;topBar&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nc"&gt;TopBar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;topBarTitle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;onBackPress&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;onBackPress&lt;/span&gt;&lt;span class="p"&gt;)&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;span class="n"&gt;paddingValues&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="nc"&gt;AndroidViewBinding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;factory&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;androidViewBindingFactory&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="n"&gt;paddingValues&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="n"&gt;update&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;androidViewBindingUpdate&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="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Navigation
&lt;/h3&gt;

&lt;p&gt;After creating the composable, we defined a navigation component with a &lt;code&gt;Screen&lt;/code&gt; enum.&lt;/p&gt;

&lt;p&gt;With above defined layout binding and &lt;code&gt;FragmentHolderScreen&lt;/code&gt; for &lt;code&gt;AnalyticsEventsFragment&lt;/code&gt;, the minimized &lt;code&gt;Scaffold&lt;/code&gt; composables look 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="nc"&gt;Scaffold&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;span class="n"&gt;contentPadding&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
        &lt;span class="nc"&gt;NavHost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;navController&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;navController&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;startDestination&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;route&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="n"&gt;contentPadding&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="nf"&gt;composable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Events&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;route&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nc"&gt;FragmentHolderScreen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                    &lt;span class="n"&gt;topBarTitle&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;R&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;toolbar_title_events&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                    &lt;span class="n"&gt;androidViewBindingFactory&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ComposeFragmentEventsBinding&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;inflate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="n"&gt;androidViewBindingUpdate&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                        &lt;span class="nf"&gt;with&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fragmentContainerViewEvents&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getFragment&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;AnalyticsEventsFragment&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;// Reference of AnalyticsEventsFragment is available here&lt;/span&gt;
                        &lt;span class="p"&gt;}&lt;/span&gt;
                    &lt;span class="p"&gt;},&lt;/span&gt;
                    &lt;span class="n"&gt;onBackPress&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                        &lt;span class="nf"&gt;onBackPress&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;navController&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="p"&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;h3&gt;
  
  
  Inner/child fragments
&lt;/h3&gt;

&lt;p&gt;Some of our fragments internally had navigation where a user would go to child fragments. While doing the compose-based navigation, we added those child fragments as part of our &lt;code&gt;Screen/route&lt;/code&gt; enum. Then, we set a lambda function (&lt;code&gt;val navigateTo: (Screen) -&amp;gt; Unit&lt;/code&gt;) in the &lt;code&gt;base&lt;/code&gt; fragment class to handle the click actions to move to child fragments.&lt;/p&gt;

&lt;h2&gt;
  
  
  SharedPreferences
&lt;/h2&gt;

&lt;p&gt;The sample app uses &lt;code&gt;SharedPreferences&lt;/code&gt;, and we didn't have time to move to something like &lt;code&gt;DataStore&lt;/code&gt;. It still worked out well after moving one screen to &lt;code&gt;Compose&lt;/code&gt;. We just made sure to define the reference of &lt;code&gt;SharedPreferences&lt;/code&gt; in &lt;code&gt;onCreate&lt;/code&gt; of the main activity and then pass the reference down to composable. Since we didn't touch existing fragments, they use the &lt;code&gt;SharedPreferences&lt;/code&gt; as defined already.&lt;/p&gt;

&lt;h2&gt;
  
  
  UI Testing
&lt;/h2&gt;

&lt;p&gt;We have extensive UI testing in the sample app that tests the SDK. After moving the sample app's home screen to &lt;code&gt;Compose&lt;/code&gt;, we broke the entry point of all our tests.&lt;/p&gt;

&lt;p&gt;Luckily, compose UI testing and espresso testing work well with each other. You can write a compose test in one-line testing the compose-based screen, and the second line can be an espresso test step looking at the view layer.&lt;/p&gt;

&lt;p&gt;The biggest problem we faced around updating unit tests was the delay between the two systems. After clicking on a button on the compose-based screen, the app would go to SDK (a view-based UI). Many tests failed because the view would not be available on the screen when a test executes. After trying out several things, nothing worked well except adding &lt;code&gt;Thread.sleep()&lt;/code&gt; just before and after we handle button clicks on the compose-based screen. The delay probably gave espresso enough time to find the correct view-based screen.&lt;/p&gt;

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

&lt;p&gt;Overall, we're satisfied with how the whole experiment went. We learned a bit about compose-view interop and got the opportunity to share our findings via this blog post.&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/shaktiman_droid" rel="noopener noreferrer"&gt;@shaktiman_droid&lt;/a&gt; on Twitter, &lt;a href="https://www.linkedin.com/in/shaktiman-droid/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; or &lt;a href="https://slack.kotlinlang.org/" rel="noopener noreferrer"&gt;Kotlin Slack&lt;/a&gt;. 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>music</category>
      <category>watercooler</category>
    </item>
    <item>
      <title>KMM: A Use case for common UI behavior</title>
      <dc:creator>Jigar Brahmbhatt</dc:creator>
      <pubDate>Mon, 30 Jan 2023 20:03:37 +0000</pubDate>
      <link>https://dev.to/touchlab/kmm-a-use-case-for-common-ui-behavior-2mi3</link>
      <guid>https://dev.to/touchlab/kmm-a-use-case-for-common-ui-behavior-2mi3</guid>
      <description>&lt;p&gt;This post demonstrates how we used &lt;code&gt;common&lt;/code&gt; KMP code for a common UI feature across three platforms.&lt;/p&gt;

&lt;h2&gt;
  
  
  Requirement
&lt;/h2&gt;

&lt;p&gt;Recently, we got a new feature requirement for the SDK we're developing using Kotlin Multiplatform (KMP). The SDK has a typical UI form with a variety of fields in it that the user fills out. One of the fields is a &lt;code&gt;phone number&lt;/code&gt; field. The requirement was to auto-format the number as the user types it out. The end behavior would be like this,&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%2F9cezytcreny6f88o0rmj.gif" 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%2F9cezytcreny6f88o0rmj.gif" alt="GIF shows phone number typed and deleted in an input field"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How we did it initially
&lt;/h2&gt;

&lt;p&gt;We usually divide KMP work based on UI stories for each platform and a task for any required work in the &lt;code&gt;commonMain&lt;/code&gt; layer.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;For deeper dive into how we divide KMP work, checkout &lt;a href="https://dev.to/touchlab/dividing-kotlin-multiplatform-work-in-teams-2cad"&gt;Kevin's post&lt;/a&gt; about it&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Following the same approach, first, we added a couple of utility methods in &lt;code&gt;common&lt;/code&gt; code to &lt;code&gt;format&lt;/code&gt; and &lt;code&gt;unformat&lt;/code&gt; the phone number.&lt;/p&gt;

&lt;h3&gt;
  
  
  commonMain
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;formatPhoneNumber&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;number&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;String&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// return formatted number using regex&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;unformatPhoneNumber&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;CharSequence&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="c1"&gt;// return unformatted number using regex&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we worked on individual platform UI implementation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Android
&lt;/h3&gt;

&lt;p&gt;We implemented a custom &lt;a href="https://developer.android.com/reference/android/text/TextWatcher" rel="noopener noreferrer"&gt;TextWatcher&lt;/a&gt; and handled the logic in &lt;code&gt;beforeTextChanged&lt;/code&gt; and &lt;code&gt;afterTextChanged&lt;/code&gt; methods.&lt;/p&gt;

&lt;h3&gt;
  
  
  iOS
&lt;/h3&gt;

&lt;p&gt;On iOS, we used &lt;code&gt;@objc func textFieldDidChange(_ sender: UITextField)&lt;/code&gt; to handle formatting while entering the number entering, and &lt;code&gt;func textField()&lt;/code&gt; with the &lt;code&gt;shouldChangeCharactersIn&lt;/code&gt; option to format the number when deleting.&lt;/p&gt;

&lt;h3&gt;
  
  
  Web
&lt;/h3&gt;

&lt;p&gt;On ReactJS, we used &lt;code&gt;onChange&lt;/code&gt; of the &lt;code&gt;TextField&lt;/code&gt; to implement custom logic for formatting.&lt;/p&gt;

&lt;h2&gt;
  
  
  What worked and what went wrong
&lt;/h2&gt;

&lt;p&gt;What worked was that each platform performed the same for the general use case of entering and deleting the number due to having common knowledge and JIRA tickets for them.&lt;/p&gt;

&lt;p&gt;What differed between each platform was how the individual developer approached edge cases around entering and deleting the input from a specific cursor position.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;For example, consider the input like this &lt;code&gt;(123) 456-7890&lt;/code&gt;. Now user wants to remove the number &lt;code&gt;6&lt;/code&gt; and manually sets the cursor position after the &lt;code&gt;dash(-)&lt;/code&gt; like this &lt;code&gt;(123) 456-|7890&lt;/code&gt;. Once the user presses the delete key, the number &lt;code&gt;6&lt;/code&gt; should be removed, and the new cursor should get positioned after the number 5 like this &lt;code&gt;(123) 45|7-890&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This logic got more complex as we found more and more edge cases at different cursor positions.&lt;/p&gt;

&lt;p&gt;What went wrong with our approach was that we started fixing these edge cases on each platform because each had unique bugs.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;common&lt;/code&gt; code at the rescue
&lt;/h2&gt;

&lt;p&gt;While fixing those edge cases on each platform, we noticed a pattern in all implementations. We were ultimately doing some string manipulation based on the current cursor position to determine a new string to show on the screen along with the new cursor position.&lt;/p&gt;

&lt;p&gt;We realized an opportunity to combine the logic in &lt;code&gt;common&lt;/code&gt; code by just introducing methods that would take existing text and cursor position as inputs and provide a new string and a cursor position back.&lt;/p&gt;

&lt;p&gt;After doing a refactoring exercise for that, we came up with two methods,&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="cm"&gt;/**
 * @param lastCursorIndex 0-based index
 */&lt;/span&gt;
&lt;span class="nd"&gt;@JsExport&lt;/span&gt;
&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;getTextAndCursorPositionOnTelephoneEnter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;lastCursorIndex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;newText&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;TelephoneFormattingResult&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * @param deletedCharIndex 0-based index
 */&lt;/span&gt;
&lt;span class="nd"&gt;@JsExport&lt;/span&gt;
&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;getTextAndCursorPositionOnTelephoneDelete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;deletedCharIndex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;oldText&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;TelephoneFormattingResult&lt;/span&gt;

&lt;span class="nd"&gt;@JsExport&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TelephoneFormattingResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nd"&gt;@JsName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"text"&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;text&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="nd"&gt;@JsName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"cursorPosition"&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;cursorPosition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Int&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;It wasn't that straightforward to come up with the inputs for the methods above because all three platforms have different ways of getting cursor indices. While entering, the index of the last cursor position before entering the new character worked out well. For the deletion, the index of the deleted character worked out better.&lt;/p&gt;

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

&lt;p&gt;We learned that just because some feature is only UI related, we shouldn't jump to the conclusion that there won't be any &lt;code&gt;common&lt;/code&gt; code. &lt;/p&gt;

&lt;p&gt;In fact, for the feature mentioned in this post, in the end, we had the most crucial logic implemented in the &lt;code&gt;common&lt;/code&gt; code. Now, each platform's implementation remains bare-bones, where we use platform-specific APIs to extract existing text/position and rely on &lt;code&gt;common&lt;/code&gt; logic to determine new text and cursor position.&lt;/p&gt;

&lt;p&gt;As a bonus, we could write extensive unit tests for the &lt;code&gt;common&lt;/code&gt; logic to cover all the edge cases. On top of that, we would have only one implementation to modify if we ever decide to change the functionality of this feature or find a bug.&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/shaktiman_droid" rel="noopener noreferrer"&gt;@shaktiman_droid&lt;/a&gt; on Twitter, &lt;a href="https://www.linkedin.com/in/shaktiman-droid/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; or &lt;a href="https://slack.kotlinlang.org/" rel="noopener noreferrer"&gt;Kotlin Slack&lt;/a&gt;. 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>ux</category>
      <category>mobile</category>
    </item>
    <item>
      <title>Different ways to distribute and integrate Kotlin/JS library</title>
      <dc:creator>Jigar Brahmbhatt</dc:creator>
      <pubDate>Mon, 11 Jul 2022 20:24:11 +0000</pubDate>
      <link>https://dev.to/touchlab/different-ways-to-distribute-and-integrate-kotlinjs-library-1hg3</link>
      <guid>https://dev.to/touchlab/different-ways-to-distribute-and-integrate-kotlinjs-library-1hg3</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Note that examples in this post use &lt;a href="https://kotlinlang.org/docs/whatsnew17.html"&gt;Kotlin 1.7.0&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In the previous &lt;a href="https://dev.to/touchlab/jsexport-guide-for-exposing-kotlin-to-js-20l9"&gt;post&lt;/a&gt; in the Kotlin/JS series, we learned about &lt;code&gt;@JsExport&lt;/code&gt; to expose Kotlin code on the JS side. Now, we would look at various ways to distribute a JS library code through a KMP setup.&lt;/p&gt;

&lt;h2&gt;
  
  
  Table Of Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
Gradle Setup

&lt;ul&gt;
&lt;li&gt;Webpack&lt;/li&gt;
&lt;li&gt;
Node Module

&lt;ul&gt;
&lt;li&gt;npm-publish&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
Using the library

&lt;ul&gt;
&lt;li&gt;
Use webpack executable

&lt;ul&gt;
&lt;li&gt;HTML&lt;/li&gt;
&lt;li&gt;JavaScript&lt;/li&gt;
&lt;li&gt;TypeScript&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
Use as node module

&lt;ul&gt;
&lt;li&gt;HTML&lt;/li&gt;
&lt;li&gt;JavaScript&lt;/li&gt;
&lt;li&gt;TypeScript&lt;/li&gt;
&lt;/ul&gt;


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

&lt;h2&gt;
  
  
  Gradle Setup&lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;If you don't already have a &lt;code&gt;JS&lt;/code&gt; target in your KMP library project, then you should check out &lt;a href="https://dev.to/touchlab/add-kotlinjs-support-to-your-kmm-library-48d9"&gt;this blog post&lt;/a&gt; first.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Let's look at common options to use for &lt;code&gt;JS&lt;/code&gt; target,&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;js&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;IR&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nf"&gt;nodejs&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;binaries&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;library&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;binaries&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;executable&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;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;browser()&lt;/strong&gt;&lt;br&gt;
It sets the JavaScript target execution environment as &lt;code&gt;browser&lt;/code&gt;. It provides a Gradle task—&lt;code&gt;jsBrowserTest&lt;/code&gt; that runs all &lt;code&gt;js&lt;/code&gt; tests inside the browser using &lt;code&gt;karma&lt;/code&gt; and &lt;code&gt;webpack&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;nodejs()&lt;/strong&gt;&lt;br&gt;
It sets the JavaScript target execution environment as &lt;code&gt;nodejs&lt;/code&gt;. It provides a Gradle task—&lt;code&gt;jsNodeTest&lt;/code&gt; that runs all &lt;code&gt;js&lt;/code&gt; tests inside &lt;code&gt;nodejs&lt;/code&gt; using the built-in test framework.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;binaries.library()&lt;/strong&gt;&lt;br&gt;
It tells the Kotlin compiler to produce Kotlin/JS code as a distributable node library. Depending on which target you've used along with this, you would get Gradle tasks to generate library distribution files.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Kotlin browser tasks
&lt;span class="nt"&gt;--------------------&lt;/span&gt;
jsBrowserDevelopmentLibraryDistribution
jsBrowserDevelopmentLibraryPrepare
jsBrowserProductionLibraryDistribution
jsBrowserProductionLibraryPrepare

Kotlin node tasks
&lt;span class="nt"&gt;-----------------&lt;/span&gt;
jsNodeDevelopmentLibraryDistribution
jsNodeDevelopmentLibraryPrepare
jsNodeDevelopmentRun
jsNodeProductionLibraryDistribution
jsNodeProductionLibraryPrepare
jsNodeProductionRun
jsNodeRun
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;Either&lt;code&gt;jsBrowserProductionLibraryDistribution&lt;/code&gt; or &lt;code&gt;jsNodeProductionLibraryDistribution&lt;/code&gt; task generates output files in &lt;code&gt;&amp;lt;YourLibModule&amp;gt;/build/productionLibrary&lt;/code&gt; folder&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;binaries.executable()&lt;/strong&gt;&lt;br&gt;
It tells the Kotlin compiler to produce Kotlin/JS code as webpack executable &lt;code&gt;.js&lt;/code&gt; files. Enabling this option generates the following Gradle tasks with the &lt;code&gt;browser()&lt;/code&gt; environment.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;jsBrowserDevelopmentExecutableDistributeResources
jsBrowserDevelopmentExecutableDistribution
jsBrowserDevelopmentRun - start development webpack dev server
jsBrowserDevelopmentWebpack - build webpack development bundle
jsBrowserDistribution
jsBrowserProductionExecutableDistributeResources
jsBrowserProductionRun - start production webpack dev server
jsBrowserProductionWebpack - build webpack production bundle
jsBrowserRun
jsBrowserWebpack
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;Task &lt;code&gt;jsBrowserDistribution&lt;/code&gt; generates webpack bundle &lt;code&gt;js&lt;/code&gt; file along with &lt;code&gt;.map&lt;/code&gt; file in &lt;code&gt;&amp;lt;YourLibModule&amp;gt;/build/distributions&lt;/code&gt; folder.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Webpack&lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Webpack is a &lt;a href="https://webpack.js.org/"&gt;JS bundler tool&lt;/a&gt;. It allows the creation of a single executable JS file combining all the dependencies.&lt;/p&gt;

&lt;p&gt;As seen above in the Gradle setup section, you can output your &lt;code&gt;Kotlin/JS&lt;/code&gt; library as a single executable by using &lt;code&gt;browser()&lt;/code&gt; and &lt;code&gt;binaries.executable()&lt;/code&gt; options in &lt;code&gt;js(IR)&lt;/code&gt; target. By default, the bundle &lt;code&gt;.js&lt;/code&gt; file and exported library name are the same as the module name. For example, for a module named &lt;em&gt;shared&lt;/em&gt;, a &lt;code&gt;shared.js&lt;/code&gt; file gets generated. It would have &lt;code&gt;exports.shared=...&lt;/code&gt; that reflects the name of the library that one would use when consuming this library in a JS/TS app.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;....."object"==typeof exports?exports.shared=n().....
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;browser()&lt;/code&gt; function provides a DSL that you can use to update &lt;a href="https://github.com/JetBrains/kotlin/blob/master/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/js/webpack/KotlinWebpack.kt"&gt;KotlinWebpack&lt;/a&gt; task for various custom configurations. For example, you can change the file and library name as below,&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;browser {
    webpackTask {
        outputFileName = "kmp_lib.js"
        output.library = "kmpLib"
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can explore more options on your own.&lt;/p&gt;

&lt;h3&gt;
  
  
  Node Module&lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Kotlin/JS library can also be published as a node module, and installed via &lt;code&gt;npm&lt;/code&gt; in an app.&lt;/p&gt;

&lt;p&gt;As mentioned above in the Gradle setup section, when you run &lt;code&gt;jsNodeProductionLibraryDistribution&lt;/code&gt;, it generates the following &lt;code&gt;node-module&lt;/code&gt; related files,&lt;br&gt;
    - &lt;code&gt;.js&lt;/code&gt; file each for the library and all dependencies&lt;br&gt;
    - &lt;code&gt;.js.map&lt;/code&gt; file for each &lt;code&gt;.js&lt;/code&gt; file&lt;br&gt;
    - Typescript &lt;code&gt;.d.ts&lt;/code&gt; file&lt;br&gt;
    - &lt;code&gt;package.json&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note&lt;/em&gt; that node module cannot have upper case character in the name. An error or warning may appear if the module name has a capital character. We can declare a &lt;code&gt;moduleName&lt;/code&gt; in the &lt;code&gt;js(IR)&lt;/code&gt; target to avoid that,&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;js(IR) {
    moduleName = "kmp-lib"
    ....
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  &lt;code&gt;npm-publish&lt;/code&gt; plugin&lt;a&gt;&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;By default, there is no quick way to publish the library output as a node module to maven.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/mpetuska/npm-publish"&gt;npm-publish&lt;/a&gt; is a popular library by &lt;a href="https://twitter.com/MartynasPetuska"&gt;Martynas Petuška&lt;/a&gt; that helps with NPM publishing. It also provides various configuration options under the &lt;code&gt;npmPublish&lt;/code&gt; Gradle task.&lt;/p&gt;

&lt;p&gt;The plugin generates a tarball that we can use to install the library locally without publishing first. The &lt;code&gt;packJsPackage&lt;/code&gt; task also logs a readable output of tarball content and details.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm notice 
npm notice 📦  shared@0.1.0
npm notice &lt;span class="o"&gt;===&lt;/span&gt; Tarball Contents &lt;span class="o"&gt;===&lt;/span&gt; 
npm notice 291B  kmp-lib.d.ts                     
npm notice 1.4kB kmp-lib.js                       
npm notice 205B  kmp-lib.js.map                   
npm notice 1.4kB kotlin-kotlin-stdlib-js-ir.js    
npm notice 551B  kotlin-kotlin-stdlib-js-ir.js.map
npm notice 95B   package.json                     
npm notice &lt;span class="o"&gt;===&lt;/span&gt; Tarball Details &lt;span class="o"&gt;===&lt;/span&gt; 
npm notice name:          shared                                  
npm notice version:       0.1.0                                   
npm notice filename:      shared-0.1.0.tgz                        
npm notice package size:  1.6 kB                                  
npm notice unpacked size: 3.9 kB                                  
shared-0.1.0.tgz
npm notice shasum:        c3c5e0a7a6d99cf1b7632fcfc4cef2e0b9052cb2
npm notice integrity:     sha512-dZa4uNPRMjyny[...]JjzzNAiP7abHw&lt;span class="o"&gt;==&lt;/span&gt;
npm notice total files:   6                                       
npm notice 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you're planning to publish a &lt;code&gt;Kotlin/JS&lt;/code&gt; library as a node module, then this is a must-have plugin in your project.&lt;/p&gt;




&lt;h2&gt;
  
  
  Using the library&lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;We will now look at various ways to integrate the Kotlin/JS library into web applications.&lt;/p&gt;

&lt;p&gt;For example, we have a &lt;code&gt;Greeting&lt;/code&gt; class and a &lt;code&gt;greet()&lt;/code&gt; method under a package &lt;code&gt;jabbar.jigariyo.kmplibrary&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;package&lt;/span&gt; &lt;span class="nn"&gt;jabbar.jigariyo.kmplibrary&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;kotlin.js.JsExport&lt;/span&gt;

&lt;span class="nd"&gt;@JsExport&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Greeting&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;greet&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;return&lt;/span&gt; &lt;span class="s"&gt;"Hello, 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;h3&gt;
  
  
  Use webpack executable&lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;We will now look at using the generated webpack binary executable bundle file in HTML and react JS app.&lt;/p&gt;

&lt;h4&gt;
  
  
  HTML&lt;a&gt;&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;We can simply embed the &lt;code&gt;js&lt;/code&gt; file as a script under the &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tag of the HTML body.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;html&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;charset=&lt;/span&gt;&lt;span class="s"&gt;"utf-8"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Including a External JavaScript File&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    &lt;span class="c"&gt;&amp;lt;!-- Here I have copied the js file in the root folder of this html file --&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text/javascript"&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"kmp_lib.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
        &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;kmpLib&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;kmpLib&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
        &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;greeting&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;kmpLib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;jabbar&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;jigariyo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;kmplibrary&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Greeting&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;greeting&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;greet&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two important things to note here are,&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Using the library name to get a reference &lt;code&gt;var kmpLib = this['kmpLib']&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Getting reference of &lt;code&gt;Greeting&lt;/code&gt; class using package name after the &lt;code&gt;library&lt;/code&gt; reference &lt;code&gt;kmpLib.jabbar.jigariyo.kmplibrary.Greeting()&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once the &lt;code&gt;html&lt;/code&gt; file is open in the browser, it would log &lt;code&gt;Hello, JS!&lt;/code&gt; in console output.&lt;/p&gt;

&lt;h4&gt;
  
  
  JavaScript&lt;a&gt;&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;For the JavaScript example, I've a React app.&lt;/p&gt;

&lt;p&gt;To keep this simple, I've copied the &lt;code&gt;distributions&lt;/code&gt; folder in the &lt;code&gt;src&lt;/code&gt; directory of the React app.&lt;/p&gt;

&lt;p&gt;As the next step, import the &lt;code&gt;library&lt;/code&gt; module in the &lt;code&gt;App.js&lt;/code&gt; and use it&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="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;kmpLib&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./distributions/kmp_lib&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;......&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;greeting&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;kmpLib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;jabbar&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;jigariyo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;kmplibrary&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Greeting&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;greeting&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;greet&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&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="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once we run the app, &lt;code&gt;Hello, JS!&lt;/code&gt; gets printed in the console and validates the integration with the library.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note that you might have to deal with configuring &lt;code&gt;eslint&lt;/code&gt; if using React.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  TypeScript&lt;a&gt;&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://tkdodo.eu/blog/calling-java-script-from-type-script"&gt;It's not straightforward&lt;/a&gt; to use just a &lt;code&gt;js&lt;/code&gt; file without typescript definitions in a &lt;code&gt;Typescript&lt;/code&gt; app.&lt;/p&gt;

&lt;p&gt;I have skipped this integration as it's out of scope for this post.&lt;/p&gt;

&lt;h3&gt;
  
  
  Use as node module&lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;We will now look at installing and using the generated library files in a React JS and a TS app.&lt;/p&gt;

&lt;p&gt;To install the module locally, you can do &lt;code&gt;npm install &amp;lt;PATH&amp;gt;&lt;/code&gt;. You can either use the path to the tarball (&lt;code&gt;.tgz&lt;/code&gt;) file generated by the &lt;code&gt;npm-publish&lt;/code&gt; plugin or the relative path to the &lt;code&gt;build/productionLibrary&lt;/code&gt; folder of the kmp module.&lt;/p&gt;

&lt;p&gt;For example, the installation path for my sample looks like this,&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install ../shared/build/productionLibrary
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once installed, the &lt;code&gt;package.json&lt;/code&gt; would contain the library under the &lt;code&gt;dependencies&lt;/code&gt; section,&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"dependencies"&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;"kmp-lib"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"file:../shared/build/productionLibrary"&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;blockquote&gt;
&lt;p&gt;Note here that the &lt;code&gt;name&lt;/code&gt; is the same as the &lt;code&gt;moduleName&lt;/code&gt; we had defined earlier in the above &lt;code&gt;gradle-setup&lt;/code&gt; section.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  HTML&lt;a&gt;&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;Using node module in plain HTML is out of scope for this post. There are techniques to make that work if you need to go that route.&lt;/p&gt;

&lt;h4&gt;
  
  
  JavaScript&lt;a&gt;&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;We should be able to &lt;code&gt;import&lt;/code&gt; the library and use it once installed via &lt;code&gt;npm&lt;/code&gt;&lt;/p&gt;

&lt;h5&gt;
  
  
  App.js
&lt;/h5&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;kmpLib&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;kmp-lib&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;......&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;greeting&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;kmpLib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;jabbar&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;jigariyo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;kmplibrary&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Greeting&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;greeting&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;greet&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&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="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once we run the app, &lt;code&gt;Hello, JS!&lt;/code&gt; gets printed in the console and validates the integration with the library.&lt;/p&gt;

&lt;h4&gt;
  
  
  TypeScript&lt;a&gt;&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;Just like the &lt;code&gt;JavaScript&lt;/code&gt; example above, you have a reference of the library available after &lt;code&gt;npm install&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Importing in &lt;code&gt;Typescript&lt;/code&gt; is slightly different as seen below&lt;/p&gt;

&lt;h5&gt;
  
  
  main.ts
&lt;/h5&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;kmpLib&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;kmp-lib&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;greeting&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;kmpLib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;jabbar&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;jigariyo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;kmplibrary&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Greeting&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;greeting&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;greet&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once we run &lt;code&gt;tsc main.ts&lt;/code&gt; and then &lt;code&gt;node main.js&lt;/code&gt; commands in the terminal, we would see &lt;code&gt;Hello, JS!&lt;/code&gt; printed.&lt;/p&gt;




&lt;p&gt;In this post, we first learned how to distribute a Kotlin/JS library as either a webpack bundle or node module. We later learned how to integrate the library in HTML, JS, or a TS app.&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/shaktiman_droid"&gt;@shaktiman_droid&lt;/a&gt; on Twitter, &lt;a href="https://www.linkedin.com/in/shaktiman-droid/"&gt;LinkedIn&lt;/a&gt; or &lt;a href="https://slack.kotlinlang.org/"&gt;Kotlin Slack&lt;/a&gt;. And if you find all this interesting, maybe you'd like to &lt;a href="https://touchlab.co/contact-us/"&gt;work with&lt;/a&gt; or &lt;a href="https://touchlab.co/careers-3/"&gt;work at&lt;/a&gt; Touchlab.&lt;/p&gt;

</description>
      <category>kotlin</category>
      <category>kotlinmultiplatform</category>
      <category>javascript</category>
      <category>kotlinjs</category>
    </item>
    <item>
      <title>@JsExport guide for exposing Kotlin to JS</title>
      <dc:creator>Jigar Brahmbhatt</dc:creator>
      <pubDate>Mon, 14 Mar 2022 16:19:31 +0000</pubDate>
      <link>https://dev.to/touchlab/jsexport-guide-for-exposing-kotlin-to-js-20l9</link>
      <guid>https://dev.to/touchlab/jsexport-guide-for-exposing-kotlin-to-js-20l9</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Note that this post focuses on JS output for Kotlin. There is also a Typescript output (.d.ts file) with some unique issues that this post doesn't cover in detail.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In the previous &lt;a href="https://dev.to/touchlab/add-kotlinjs-support-to-your-kmm-library-48d9"&gt;post&lt;/a&gt;, we added &lt;code&gt;Kotlin/JS&lt;/code&gt; support to an existing KMM library. Now, we would add code that works on the JS side.&lt;/p&gt;

&lt;h2&gt;
  
  
  Table Of Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
Usage

&lt;ul&gt;
&lt;li&gt;@ExperimentalJsExport vs @JsExport&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

Limitations

&lt;ul&gt;
&lt;li&gt;Collections&lt;/li&gt;
&lt;li&gt;Long&lt;/li&gt;
&lt;li&gt;
Interface

&lt;ul&gt;
&lt;li&gt;Solution - Using Implementation class&lt;/li&gt;
&lt;li&gt;Solution - Using Expect-Actual&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Enum&lt;/li&gt;

&lt;li&gt;Sealed classes&lt;/li&gt;

&lt;li&gt;Code mangling&lt;/li&gt;

&lt;li&gt;Suspended functions&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Usage &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;It is critical to understand &lt;a href="https://kotlinlang.org/docs/js-to-kotlin-interop.html#jsexport-annotation" rel="noopener noreferrer"&gt;@JsExport&lt;/a&gt; annotation and all the issues around it if you expose Kotlin code through Kotlin/JS as an external JS library&lt;/p&gt;

&lt;p&gt;With the new &lt;a href="https://kotlinlang.org/docs/js-ir-compiler.html" rel="noopener noreferrer"&gt;IR compiler&lt;/a&gt;, &lt;strong&gt;Kotlin declarations do not get exposed to JavaScript by default&lt;/strong&gt;. To make Kotlin declarations visible to JavaScript, they &lt;strong&gt;must be&lt;/strong&gt; annotated with @JsExport.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note that @JsExport is experimental as of the posted date of this post (with Kotlin 1.6.10)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Let’s start with a very basic example,&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;

&lt;span class="c1"&gt;// commonMain - Greeting.kt&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Greeting&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;greeting&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;return&lt;/span&gt; &lt;span class="s"&gt;"Hello World!"&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;At this point, the generated &lt;code&gt;.js&lt;/code&gt; library file would not have any reference to the Greeting class. The reason is that it is missing the &lt;code&gt;@JsExport&lt;/code&gt; annotation.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You can generate JS library code via &lt;code&gt;./gradlew jsBrowserDistribution&lt;/code&gt;. You would find the &lt;code&gt;.js, .d.ts and map&lt;/code&gt; file in &lt;code&gt;root/build/js/packages/&amp;lt;yourlibname&amp;gt;/kotlin&lt;/code&gt; folder.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now, add the annotation to generate JS code for it,&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.ExperimentalJsExport&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;kotlin.js.JsExport&lt;/span&gt;

&lt;span class="nd"&gt;@ExperimentalJsExport&lt;/span&gt;
&lt;span class="nd"&gt;@JsExport&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Greeting&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;greeting&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;return&lt;/span&gt; &lt;span class="s"&gt;"Hello World!"&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;The &lt;code&gt;.js&lt;/code&gt; and &lt;code&gt;.d.ts&lt;/code&gt; files would now contain the Greeting reference.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Generated .js file&lt;/strong&gt;
```javascript
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;function Greeting() {&lt;br&gt;
}&lt;br&gt;
Greeting.prototype.greeting = function () {&lt;br&gt;
  return 'Hello World!';&lt;br&gt;
};&lt;br&gt;
Greeting.$metadata$ = {&lt;br&gt;
  simpleName: 'Greeting',&lt;br&gt;
  kind: 'class',&lt;br&gt;
  interfaces: []&lt;br&gt;
};&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
- **Generated .d.ts file**
```typescript


export namespace jabbar.jigariyo.kmplibrary {
    class Greeting {
        constructor();
        greeting(): string;
    }
}


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

&lt;/div&gt;

&lt;p&gt;Now you can call &lt;code&gt;Greeting&lt;/code&gt; from JavaScript&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;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;jabbar&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;jigariyo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;kmplibrary&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Greeting&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;greeting&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="c1"&gt;// Hello World!&lt;/span&gt;


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

&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;Note that you would have to use fully qualified Kotlin names in JavaScript because Kotlin exposes its &lt;a href="https://kotlinlang.org/docs/js-to-kotlin-interop.html#package-structure" rel="noopener noreferrer"&gt;package structure&lt;/a&gt; to JavaScript.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It is &lt;strong&gt;important&lt;/strong&gt; to keep in mind that all public attributes in your exportable object would also need to be exportable.&lt;/p&gt;

&lt;p&gt;In the following example, &lt;code&gt;CustomObj&lt;/code&gt; would also need to be exportable to export &lt;code&gt;MyDataClass&lt;/code&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;@JsExport&lt;/span&gt;
&lt;span class="kd"&gt;data class&lt;/span&gt; &lt;span class="nc"&gt;MyDataClass&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;strVal&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;val&lt;/span&gt; &lt;span class="py"&gt;customObj&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;CustomObj&lt;/span&gt; &lt;span class="c1"&gt;// This would need to be exportable&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;


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

&lt;/div&gt;
&lt;h3&gt;
  
  
  @ExperimentalJsExport vs @JsExport &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;@JsExport&lt;/code&gt; is the annotation you need to tell the compiler to generate JavaScript code, and &lt;code&gt;@ExperimentalJsExport&lt;/code&gt; is an opt-in marker annotation to use &lt;code&gt;@JsExport&lt;/code&gt; as it is experimental to use.&lt;/p&gt;

&lt;p&gt;You can get rid of the requirement of adding &lt;code&gt;@ExperimentalJsExport&lt;/code&gt; in code by declaring it as &lt;code&gt;OptIn&lt;/code&gt; in &lt;code&gt;languageSettings&lt;/code&gt; for all source sets in your &lt;code&gt;kotlin&lt;/code&gt; block.&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;kotlin&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;sourceSets&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;all&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;languageSettings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;apply&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nf"&gt;optIn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"kotlin.js.ExperimentalJsExport"&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="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;
  
  
  Limitations &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;As of Kotlin &lt;code&gt;1.6.10&lt;/code&gt;, there are heavy limitations on what Kotlin types one can export to JavaScript.&lt;/p&gt;

&lt;p&gt;You will most likely face one of these limitations if you add JS support in an existing KMP library.&lt;/p&gt;

&lt;p&gt;Whenever something is &lt;code&gt;not-exportable&lt;/code&gt;, you would get either an error or a warning:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Code does not compile with such errors
&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%2Fdzqmbsmgtl56c4u4syww.png" alt="Js exportable error example"&gt;
&lt;/li&gt;
&lt;li&gt;Code compiles with such warnings, but you might have run-time issues
&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%2Fh7kzdox841kzdl4heqoj.png" alt="JS exportable warning"&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Collections &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Kotlin's collections APIs are not exportable, so you would have to come up with different strategies to deal with them. Some examples would be:&lt;/p&gt;

&lt;h4&gt;
  
  
  Map
&lt;/h4&gt;

&lt;p&gt;You would have to remove &lt;code&gt;Map&lt;/code&gt; usage from &lt;code&gt;common&lt;/code&gt; code that also exports to JS, or you would have to have a different implementation on the &lt;code&gt;mobile&lt;/code&gt; and &lt;code&gt;js&lt;/code&gt; side. You can use the &lt;code&gt;kotlin.js.Json&lt;/code&gt; object on the &lt;code&gt;jsMain&lt;/code&gt; side and then map it to the &lt;code&gt;Kotlin&lt;/code&gt; map whenever needed.&lt;/p&gt;

&lt;p&gt;For JS specific implementation, you may also look into using &lt;a href="https://github.com/JetBrains/kotlin-wrappers/blob/master/kotlin-js/src/main/kotlin/kotlinx/js/Record.kt" rel="noopener noreferrer"&gt;Record&lt;/a&gt; from &lt;a href="https://github.com/JetBrains/kotlin-wrappers/tree/master/kotlin-js" rel="noopener noreferrer"&gt;kotlin-extensions&lt;/a&gt; library.&lt;/p&gt;

&lt;h4&gt;
  
  
  List
&lt;/h4&gt;

&lt;p&gt;You can replace the &lt;code&gt;List&lt;/code&gt; usage with an &lt;code&gt;Array&lt;/code&gt; to keep the same code for all platforms. It may or may not be a simple replacement.&lt;/p&gt;

&lt;p&gt;For example, &lt;code&gt;Array&lt;/code&gt; would work if only used in an object for parsing an API response. Note that having an &lt;code&gt;Array&lt;/code&gt; in a &lt;code&gt;Data&lt;/code&gt; class would require providing your own &lt;code&gt;equals&lt;/code&gt; and &lt;code&gt;hashcode&lt;/code&gt; implementations.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note that moving from &lt;code&gt;List&lt;/code&gt; to &lt;code&gt;Array&lt;/code&gt; might have an impact on generated code for &lt;code&gt;iOS&lt;/code&gt;. &lt;code&gt;List&lt;/code&gt; becomes &lt;code&gt;NSArray&lt;/code&gt; on iOS side but &lt;code&gt;Array&lt;/code&gt; becomes a Kotlin object wrapping the array&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you want separate implementation for &lt;code&gt;jsMain&lt;/code&gt;, then &lt;code&gt;kotlin-extensions&lt;/code&gt; library provides some helpful JS specific classes like &lt;a href="https://github.com/JetBrains/kotlin-wrappers/tree/master/kotlin-js/src/main/kotlin/kotlinx/js" rel="noopener noreferrer"&gt;Iterator, Set, and ReadOnlyArray&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Long &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;Long&lt;/code&gt; is not mapped to anything as there is no equivalent in the &lt;code&gt;JavaScript&lt;/code&gt; world. You would see the &lt;code&gt;non-exportable&lt;/code&gt; warning if you export &lt;code&gt;Long&lt;/code&gt; via &lt;code&gt;Kotlin&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If you ignore the warning, then &lt;code&gt;Long&lt;/code&gt; still kinda works. It just takes any value from JS. Kotlin will receive the input as &lt;code&gt;Long&lt;/code&gt; if JavaScript code sends a &lt;code&gt;BigInt&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;It will not work for &lt;code&gt;Typescript&lt;/code&gt; unless you set &lt;code&gt;skipLibCheck = true&lt;/code&gt; in the config as type &lt;code&gt;kotlin.Long&lt;/code&gt; is not available.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;

&lt;span class="c1"&gt;// Kotlin &lt;/span&gt;
&lt;span class="nd"&gt;@JsExport&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Greeting&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@Suppress&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"NON_EXPORTABLE_TYPE"&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;printLong&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt;&lt;span class="p"&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="n"&gt;value&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="c1"&gt;// Generated .js&lt;/span&gt;
&lt;span class="nc"&gt;Greeting&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;prototype&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;printLong&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&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="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="c1"&gt;// Generated .d.ts&lt;/span&gt;
&lt;span class="nf"&gt;printLong&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;kotlin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Long&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="n"&gt;void&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Usage from JS&lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0b11111111111111111111111111111111111111111111111111111"&lt;/span&gt;
&lt;span class="nc"&gt;Greeting&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;printLong&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;BigInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="c1"&gt;// This works&lt;/span&gt;



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

&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;You can use @Suppress("NON_EXPORTABLE_TYPE") to suppress the exportable warning&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Interface &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Kotlin interfaces are not exportable. It gets annoying when a library has an interface-driven design, where it exposes the interface in public API rather than a specific implementation.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Interfaces will be exportable starting upcoming &lt;a href="https://youtrack.jetbrains.com/issue/KT-45434" rel="noopener noreferrer"&gt;Kotlin 1.6.20&lt;/a&gt;! We would have to play around with that to see it working.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;There are workarounds to make interfaces work on &lt;code&gt;JavaScript&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The following are some examples for getting around interfaces:&lt;/p&gt;

&lt;h4&gt;
  
  
  Using Implementation Class &lt;a&gt;&lt;/a&gt;
&lt;/h4&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;

&lt;span class="nd"&gt;@JsExport&lt;/span&gt;
&lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;HelloInterface&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;hello&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;The above code would show the non-exportable error. You can use the &lt;code&gt;interface&lt;/code&gt; indirectly via its implementation class to work around that problem.&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;@JsExport&lt;/span&gt;
&lt;span class="kd"&gt;object&lt;/span&gt; &lt;span class="nc"&gt;Hello&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;HelloInterface&lt;/span&gt; &lt;span class="p"&gt;{&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;hello&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"HELLO from HelloInterface"&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;blockquote&gt;
&lt;p&gt;Generated JS code for the above &lt;code&gt;hello&lt;/code&gt; method will have a &lt;code&gt;mangled&lt;/code&gt; name. Read more about it in code-mangling section&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;

&lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;HelloInterface&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@JsName&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="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;hello&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;@JsExport&lt;/span&gt;
&lt;span class="kd"&gt;object&lt;/span&gt; &lt;span class="nc"&gt;Hello&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;HelloInterface&lt;/span&gt; &lt;span class="p"&gt;{&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;hello&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"HELLO from HelloInterface"&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;Similarly, here are some variations to use &lt;code&gt;HelloInterface&lt;/code&gt;,&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;

&lt;span class="c1"&gt;// Variation (2)&lt;/span&gt;
&lt;span class="nd"&gt;@JsExport&lt;/span&gt;
&lt;span class="kd"&gt;object&lt;/span&gt; &lt;span class="nc"&gt;HelloGet&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;getInterface&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nc"&gt;HelloInterface&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Hello&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Variation (3)&lt;/span&gt;
&lt;span class="nd"&gt;@JsExport&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;HelloWrapper&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;@JsName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"value"&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;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;HelloInterface&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// Variation (4)&lt;/span&gt;
&lt;span class="nd"&gt;@JsExport&lt;/span&gt;
&lt;span class="kd"&gt;data class&lt;/span&gt; &lt;span class="nc"&gt;HelloWrapperData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;@JsName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"value"&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;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;HelloInterface&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;



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

&lt;/div&gt;

&lt;p&gt;All above variations are usable from the &lt;code&gt;JS&lt;/code&gt; side even with a &lt;code&gt;non-exportable&lt;/code&gt; warning around interface usage,&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="cm"&gt;/**
 * JS side calling code
 * (1)
 * Hello.hello()
 *
 * (2)
 * HelloGet.getInterface().hello()
 *
 * (3)
 * const wrapperObj = HelloWrapper(Hello)
 * wrapperObj.value.hello()
 *
 * (4)
 * const wrapperDataObj = HelloWrapperData(Hello)
 * wrapperDataObj.value.hello()
 */&lt;/span&gt;


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

&lt;/div&gt;
&lt;h4&gt;
  
  
  Using Expect-Actual Pattern &lt;a&gt;&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;Another idea for using interfaces is to use the &lt;code&gt;expect-actual&lt;/code&gt; pattern to define a Kotlin interface in &lt;code&gt;common&lt;/code&gt; and &lt;code&gt;mobile&lt;/code&gt; platforms and define an &lt;code&gt;external interface&lt;/code&gt; for the JS side. This approach might not scale well but can be very useful for simple cases.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;

&lt;span class="c1"&gt;// commonMain&lt;/span&gt;
&lt;span class="n"&gt;expect&lt;/span&gt; &lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;Api&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;getProfile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Profile&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="c1"&gt;// jsMain&lt;/span&gt;
&lt;span class="c1"&gt;// Here external makes it a normal JS object in generated code&lt;/span&gt;
&lt;span class="n"&gt;actual&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;Api&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;actual&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;getProfile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Profile&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="c1"&gt;// mobileMain&lt;/span&gt;
&lt;span class="n"&gt;actual&lt;/span&gt; &lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;Api&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;actual&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;getProfile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Profile&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;These examples showcase workarounds that might or might not work for a particular project. &lt;/p&gt;

&lt;h3&gt;
  
  
  Enum &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;As of Kotlin 1.6.10, enums are not exportable. It can create issues for projects that have a lot of existing enums. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Good news is that its support coming in Kotlin 1.6.20&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;There is also a trick to export and use enums on JS. It requires defining a JS-specific object with attributes that point to actual enums.&lt;/p&gt;

&lt;p&gt;For example, this code won't compile,&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;@JsExport&lt;/span&gt;
&lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="nc"&gt;Gender&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;MALE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nc"&gt;FEMALE&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Instead, you can do this indirectly by re-defining them through object fields. It works with a non-exportable warning. Note the warning suppression with annotation.&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;@Suppress&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"NON_EXPORTABLE_TYPE"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nd"&gt;@ExperimentalJsExport&lt;/span&gt;
&lt;span class="nd"&gt;@JsExport&lt;/span&gt;
&lt;span class="kd"&gt;object&lt;/span&gt; &lt;span class="nc"&gt;GenderType&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;male&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Gender&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;MALE&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;female&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Gender&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;FEMALE&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


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

&lt;/div&gt;
&lt;h3&gt;
  
  
  Sealed classes &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Sealed classes are exportable, but they’re buggy as of Kotlin 1.6.10&lt;/p&gt;

&lt;p&gt;You can export a data or regular class as subclasses inside a Sealed class body, but not an object. &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;@JsExport&lt;/span&gt;
&lt;span class="k"&gt;sealed&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;State&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;object&lt;/span&gt; &lt;span class="nc"&gt;Loading&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;State&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;// This won't be visible &lt;/span&gt;
    &lt;span class="kd"&gt;data class&lt;/span&gt; &lt;span class="nc"&gt;Done&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;value&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;State&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;// This would be visible&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;You can work around this problem by moving the subclasses outside the body of the sealed class, but then you cannot write it like &lt;code&gt;State.Loading&lt;/code&gt;. It is more of a readability issue in that case.&lt;/p&gt;

&lt;p&gt;Also, sealed classes have known issues with typescript binding as well.&lt;/p&gt;

&lt;h3&gt;
  
  
  Code mangling &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;The Kotlin compiler mangles the names of the functions and attributes. It can be frustrating to deal with mangled names.&lt;/p&gt;

&lt;p&gt;For example,&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;@JsExport&lt;/span&gt;
&lt;span class="kd"&gt;object&lt;/span&gt; &lt;span class="nc"&gt;Hello&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;HelloInterface&lt;/span&gt; &lt;span class="p"&gt;{&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;hello&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"HELLO from HelloInterface"&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;Generated JS code for &lt;code&gt;hello&lt;/code&gt; method looks like,&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;Hello&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prototype&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hello_sv8swh_k$&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;HELLO from HelloInterface&lt;/span&gt;&lt;span class="dl"&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;We would need to use the &lt;a href="https://kotlinlang.org/docs/js-to-kotlin-interop.html#jsname-annotation" rel="noopener noreferrer"&gt;@JsName&lt;/a&gt; annotation to provide a generated name. If you see numbers in attribute names like &lt;code&gt;_something_0, _value_3&lt;/code&gt; on the JS side, then it is a sign that you need to provide a controlled name via &lt;code&gt;@JsName&lt;/code&gt; annotation on the &lt;code&gt;Kotlin&lt;/code&gt; side.&lt;/p&gt;

&lt;p&gt;After adding &lt;code&gt;@JsName("hello")&lt;/code&gt; in the above example, generated code looks like this where there is a new &lt;code&gt;hello&lt;/code&gt; method that references &lt;code&gt;hello_sv8swh_k$&lt;/code&gt; internally,&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;Hello&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prototype&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hello_sv8swh_k$&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;HELLO from HelloInterface&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="nx"&gt;Hello&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prototype&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hello&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hello_sv8swh_k&lt;/span&gt;&lt;span class="nf"&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;blockquote&gt;
&lt;p&gt;Note that &lt;code&gt;@JsName&lt;/code&gt; is prohibited for overridden members, so you would need to set it to a base class property or method.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Suspended functions &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;You cannot expose suspended functions to JS. You would need to convert them into &lt;code&gt;JavaScript Promise&lt;/code&gt; object.&lt;/p&gt;

&lt;p&gt;The easiest way to do that would be to wrap suspend calls inside,&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

GlobalScope.promise {
  // suspend call
}


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

&lt;/div&gt;

&lt;p&gt;This function comes from &lt;code&gt;Promise.kt&lt;/code&gt; in the &lt;code&gt;coroutine library&lt;/code&gt;. It returns a generic type.&lt;/p&gt;




&lt;p&gt;As mentioned earlier, some of these issues would get resolved with Kotlin 1.6.20, so keep that in mind.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;In the next post, we will look at different ways to distribute Kotlin/JS library since we've some JS exportable code.&lt;/em&gt;&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/shaktiman_droid" rel="noopener noreferrer"&gt;shaktiman_droid&lt;/a&gt; on Twitter, &lt;a href="https://www.linkedin.com/in/shaktiman-droid/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; or &lt;a href="https://slack.kotlinlang.org/" rel="noopener noreferrer"&gt;Kotlin Slack&lt;/a&gt;. 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>kotlinjs</category>
      <category>kotlin</category>
      <category>kotlinmultiplatform</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Add Kotlin/JS support to your KMM library</title>
      <dc:creator>Jigar Brahmbhatt</dc:creator>
      <pubDate>Fri, 04 Mar 2022 22:23:52 +0000</pubDate>
      <link>https://dev.to/touchlab/add-kotlinjs-support-to-your-kmm-library-48d9</link>
      <guid>https://dev.to/touchlab/add-kotlinjs-support-to-your-kmm-library-48d9</guid>
      <description>&lt;p&gt;We have been working on a few projects that need to expose Kotlin code through &lt;a href="https://kotlinlang.org/docs/js-overview.html" rel="noopener noreferrer"&gt;Kotlin/JS&lt;/a&gt; as an external JS library. You can add JS as an output target of an existing KMM-focused module, but there are some issues you'll need to consider that don't generally present challenges to a mobile-only project.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note that this post assumes that you already have a Kotlin Multiplatform Mobile library project, and are planning to add Kotlin/JS support to it&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A good f̵i̵r̵s̵t̵ zero-step (not a mandatory one) would be to make sure that your source sets are marked &lt;code&gt;by getting&lt;/code&gt; as per the Kotlin Gradle DSL standards. It only applies if you use &lt;code&gt;Kotlin&lt;/code&gt; based &lt;code&gt;build scripts&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I would strongly recommend moving to &lt;code&gt;Kotlin&lt;/code&gt; based scripts If you're still using &lt;code&gt;Groovy&lt;/code&gt;-based Gradle build scripts &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This &lt;a href="https://kotlinlang.org/docs/mpp-dsl-reference.html" rel="noopener noreferrer"&gt;Multiplatform Gradle DSL reference&lt;/a&gt; is a helpful document to follow while writing a Gradle build script for KMP.&lt;/p&gt;

&lt;p&gt;After this step, your build script would have source sets declared as below,&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;kotlin&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="nf"&gt;sourceSets&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;commonMain&lt;/span&gt; &lt;span class="k"&gt;by&lt;/span&gt; &lt;span class="nf"&gt;getting&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&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;You may check out &lt;a href="https://github.com/touchlab/KaMPKit/commit/c7aea3ab33d495c4ec96b32ae9a49193ac8bcd69" rel="noopener noreferrer"&gt;this commit&lt;/a&gt; where I made these changes for the &lt;code&gt;KaMPKit&lt;/code&gt; project&lt;/p&gt;

&lt;p&gt;Now let's move to actual steps&lt;/p&gt;




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

&lt;p&gt;Make sure that you &lt;strong&gt;remove&lt;/strong&gt; any &lt;code&gt;clean&lt;/code&gt; task from your project. Gradle's &lt;code&gt;LifecycleBasePlugin&lt;/code&gt; already brings the &lt;code&gt;clean&lt;/code&gt; task, so you want to avoid getting a compilation error later. You most likely have one in your root Gradle file that looks like this,&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;tasks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;register&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Delete&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="s"&gt;"clean"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rootProject&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;buildDir&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;Add the JS target block with &lt;code&gt;IR&lt;/code&gt; compiler option to your &lt;code&gt;kotlin&lt;/code&gt; block, and add the &lt;code&gt;nodejs&lt;/code&gt; target and &lt;code&gt;library&lt;/code&gt; container inside that&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;We will discuss both options in detail later&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;

&lt;span class="nf"&gt;kotlin&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// .... other targets&lt;/span&gt;
    &lt;span class="nf"&gt;js&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;IR&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;nodejs&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;binaries&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;library&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;Add &lt;code&gt;main&lt;/code&gt; and &lt;code&gt;test&lt;/code&gt; source sets for JS&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;sourceSets&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// .... other source sets&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;jsMain&lt;/span&gt; &lt;span class="k"&gt;by&lt;/span&gt; &lt;span class="n"&gt;getting&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;jsTest&lt;/span&gt; &lt;span class="k"&gt;by&lt;/span&gt; &lt;span class="nf"&gt;getting&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;dependencies&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// you don't need this if you already have&lt;/span&gt;
            &lt;span class="c1"&gt;// kotlin("test") as your `commonTest` dependency&lt;/span&gt;
            &lt;span class="nf"&gt;implementation&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;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;If you &lt;code&gt;sync&lt;/code&gt; the project now, it should sync successfully! &lt;em&gt;(probably won't build yet)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Now add the actual &lt;code&gt;JS&lt;/code&gt; source folders. &lt;/p&gt;

&lt;p&gt;Since we've already added &lt;code&gt;JS&lt;/code&gt; target, we can add the &lt;code&gt;jsMain&lt;/code&gt; and &lt;code&gt;jsTest&lt;/code&gt; directories using auto complete by right-clicking on &lt;code&gt;src&lt;/code&gt; --&amp;gt; &lt;code&gt;new&lt;/code&gt; --&amp;gt; &lt;code&gt;Directory&lt;/code&gt;&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%2F4kuxnb4j9a5rszb7ognm.gif" 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%2F4kuxnb4j9a5rszb7ognm.gif" alt="Animation to show how to add JavaScript source set"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2
&lt;/h2&gt;

&lt;p&gt;At this stage, your project might not compile if you have any code in &lt;code&gt;commonMain&lt;/code&gt; that Kotlin/JS does not support, or if it is missing JS equivalents. &lt;code&gt;./gradlew build&lt;/code&gt; would most likely fail.&lt;/p&gt;

&lt;p&gt;You now have two options,&lt;/p&gt;

&lt;p&gt;1) Make sure all your common code compiles for JS, can be exported as JS library and add &lt;code&gt;js&lt;/code&gt; actual for any &lt;code&gt;expect&lt;/code&gt; declarations&lt;/p&gt;

&lt;p&gt;2) Introduce a &lt;code&gt;mobileMain&lt;/code&gt; source set/folder and move all the existing common code there&lt;/p&gt;

&lt;p&gt;I would suggest going with &lt;code&gt;option (2)&lt;/code&gt; because it is a path of the least resistance, and you would get more time to think about how you want to write JS-specific code and common code for all platforms later. You may already have a lot of existing code in &lt;code&gt;commonMain&lt;/code&gt; with various dependencies that might not be suitable to use on JS. &lt;/p&gt;

&lt;p&gt;Native mobile platforms, &lt;code&gt;Android&lt;/code&gt; and &lt;code&gt;iOS&lt;/code&gt; tend to have similar needs and capabilities like SQL, local files, threads, serialization, etc. JS/web, on the other hand, are somewhat different in what you can do and often work differently. It makes sense then that any moderately functional library will need at least consideration of the conceptual differences and quite probably another layer (&lt;code&gt;mobileMain&lt;/code&gt;) to better separate features and dependencies between &lt;code&gt;web&lt;/code&gt; and &lt;code&gt;native mobile&lt;/code&gt;. The latter is generally what we recommend, because you’ll probably need to do that separation at some point anyway.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;option (1)&lt;/code&gt; is the fallback if you have a small existing codebase that requires only a few changes to support the JS side, and if you would most likely not have any common code between &lt;code&gt;android&lt;/code&gt; and &lt;code&gt;iOS&lt;/code&gt; platforms.&lt;/p&gt;

&lt;h3&gt;
  
  
  For option 2
&lt;/h3&gt;

&lt;p&gt;First, you would need to create custom source sets for &lt;code&gt;mobileMain&lt;/code&gt; and &lt;code&gt;mobileTest&lt;/code&gt; and make &lt;code&gt;android&lt;/code&gt; and &lt;code&gt;ios&lt;/code&gt; ones depend on it. Also, note that you will need to move all dependencies from &lt;code&gt;commonMain&lt;/code&gt; to &lt;code&gt;mobileMain&lt;/code&gt;. In short, you would want &lt;code&gt;mobileMain&lt;/code&gt; to look like your &lt;code&gt;commonMain&lt;/code&gt; after the change. &lt;code&gt;commonMain&lt;/code&gt; would get emptied.&lt;/p&gt;

&lt;p&gt;Before and after diff of mentioned changes look like this for a sample project,&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="err"&gt;

&lt;/span&gt;     sourceSets {
&lt;span class="gd"&gt;-        val commonMain by getting {
-            dependencies {
-                implementation("io.ktor:ktor-client-core:$ktorVersion")
-            }
-        }
&lt;/span&gt;&lt;span class="gi"&gt;+        val commonMain by getting
&lt;/span&gt;         val commonTest by getting {
             dependencies {
                 implementation(kotlin("test"))
             }
         }
         val iosArm64Main by getting
         val iosSimulatorArm64Main by getting
         val iosMain by creating {
&lt;span class="gd"&gt;-            dependsOn(commonMain)
&lt;/span&gt;             iosX64Main.dependsOn(this)
             iosArm64Main.dependsOn(this)
             iosSimulatorArm64Main.dependsOn(this)
         }
         val iosArm64Test by getting
         val iosSimulatorArm64Test by getting
         val iosTest by creating {
&lt;span class="gd"&gt;-            dependsOn(commonTest)
&lt;/span&gt;             iosX64Test.dependsOn(this)
             iosArm64Test.dependsOn(this)
             iosSimulatorArm64Test.dependsOn(this)
         }
&lt;span class="gi"&gt;+        val mobileMain by creating {
+            dependsOn(commonMain)
+            androidMain.dependsOn(this)
+            iosMain.dependsOn(this)
+            dependencies {
+                implementation("io.ktor:ktor-client-core:$ktorVersion")
+            }
+        }
+        val mobileTest by creating {
+            dependsOn(commonTest)
+            androidTest.dependsOn(this)
+            iosTest.dependsOn(this)
+        }
&lt;/span&gt;         val jsMain by getting
         val jsTest by getting
     }
&lt;span class="err"&gt;

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

&lt;/div&gt;

&lt;p&gt;Next, you would add the actual folder, just like what we did for &lt;code&gt;js&lt;/code&gt; above in &lt;code&gt;step 1&lt;/code&gt;. Along with that, you would want to move the entire &lt;code&gt;commonMain&lt;/code&gt; code content to &lt;code&gt;mobileMain&lt;/code&gt;, and &lt;code&gt;commonTest&lt;/code&gt; to &lt;code&gt;mobileTest&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/ozrEJybnR8g"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;After this, your project should build successfully with &lt;code&gt;./gradlew build&lt;/code&gt; because you would not have any code in &lt;code&gt;commonMain&lt;/code&gt; that failed before due to introduction of JS side.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3
&lt;/h2&gt;

&lt;p&gt;Now you're ready to work on the JS codebase.&lt;/p&gt;

&lt;p&gt;You might end up moving some classes back from &lt;code&gt;mobileMain&lt;/code&gt; to &lt;code&gt;commonMain&lt;/code&gt; depending on what you want in all three platforms, but at this point you can build and test the project after every step so that you're sure nothing is breaking.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Now that you have the &lt;code&gt;JS&lt;/code&gt; sourceSet, in the next post we will look at writing exportable code in Kotlin for JS using &lt;code&gt;@JsExport&lt;/code&gt;&lt;/em&gt;&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/shaktiman_droid" rel="noopener noreferrer"&gt;shaktiman_droid&lt;/a&gt; on Twitter, &lt;a href="https://www.linkedin.com/in/shaktiman-droid/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; or &lt;a href="https://slack.kotlinlang.org/" rel="noopener noreferrer"&gt;Kotlin Slack&lt;/a&gt;. 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>kotlinjs</category>
      <category>kotlin</category>
      <category>kotlinmultiplatform</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
