Chapters

Hide chapters

Kotlin Multiplatform by Tutorials

First Edition · Android 12, iOS 15, Desktop · Kotlin 1.6.10 · Android Studio Bumblebee

14. Creating Your KMP Library
Written by Carlos Mota

Heads up... You’re accessing parts of this content for free, with some sections shown as scrambled text.

Heads up... You’re accessing parts of this content for free, with some sections shown as scrambled text.

Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.

Unlock now

In the previous chapters, you’ve built learn for Android, iOS and desktop. All of these apps fetch the raywenderlich.com RSS feed and show you the latest articles written about Android, iOS, Flutter and Unity. You can search for a specific topic or save an article locally to read it later. During the app’s development process, you’ve worked with:

  • Serialization
  • Networking
  • Databases
  • Concurrency

And, along this journey, you’ve also built additional tools that can be reused in other projects:

  • Logger
  • Dispatchers

In this chapter, you’re going to learn how you can create and publish a library so you can reuse it in the other apps that you develop in this book — and for the next one you’re going to build. :]

Migrating an existing feature to Multiplatform

Throughout this book, you’ve learned how to develop a project that had a library already shared across different platforms. However, you may want to migrate an existing app to Kotlin Multiplatform.

In this section, you’re going to see how a simple feature like opening a website link in a browser can easily be moved to KMP.

Learning how to open a link in different platforms

In learn, when you click on an article, a web page opens — whether it’s on Android, iOS or desktop. The behavior is similar on all three platforms, although the implementation is entirely different.

private fun openEntry(url: String) {
  val intent = Intent(Intent.ACTION_VIEW)
  intent.data = Uri.parse(url)
  startActivity(intent)
}
@Environment(\.openURL) var openURL
openURL(URL(string: "\(item.link)")!)
fun openEntry(url: String) {
  try {
    val desktop = Desktop.getDesktop()
    desktop.browse(URI.create(url))
  } catch(e: Exception) {
    Logger.e(TAG, "Unable to open url. Reason: ${e.stackTrace}")
  }
}

Adding a new module

The first thing to decide is if you want to move this logic to the existing shared module or create a new one. Since adding a new library also requires you to migrate the code, you’re going with this more complete solution.

include(":shared-action")
jvm("desktop")
val desktopMain by getting
Fig. 14.1 - Project structure
Leh. 40.7 - Sqedort pxfepxuso

Fig. 14.2 - Android Studio project view
Bap. 50.3 - Umdqaoc Ftabou mrumadv qoeq

Configuring an Android library to publish

To publish the Android libraries, you need to make an update to the android() target definition in the kotlin section of the build.gradle.kts file from shared-action to:

android {
  publishLibraryVariants("release", "debug")
}

Configuring a Multiplatform Swift package

You have different possibilities to generate a library. Since Apple has its own package manager — Swift Package Manager — and many libraries are now available through it, you’re going to use it in this chapter.

includeBuild("plugins/multiplatform-swiftpackage-m1_support")
id("com.chromaticnoise.multiplatform-swiftpackage-m1-support")
//1
multiplatformSwiftPackage {
  //2
  xcframeworkName("SharedAction")
  //3
  swiftToolsVersion("5.3")
  //4
  targetPlatforms {
    iOS { v("13") }
  }
  //5
  outputDirectory(File(projectDir, "sharedaction"))
}
val xcf = XCFramework("SharedAction")
listOf(
  iosX64(),
  iosArm64(),
  iosSimulatorArm64()
).forEach {
  it.binaries.framework {
    baseName = "SharedAction"
    xcf.add(this)
  }
}
./gradlew shared-action:createSwiftPackage
id("com.chromaticnoise.multiplatform-swiftpackage-m1-support")
multiplatformSwiftPackage {
  xcframeworkName("SharedKit")
  swiftToolsVersion("5.3")
  targetPlatforms {
    iOS { v("13") }
  }
}
./gradlew createSwiftPackage

Migrating the code to Multiplatform

Now that you’ve got everything configured, it’s time to move the code from the app’s UI to Multiplatform. Since the shared-action module is going to deal with the user action of opening a link, on the PlatformAction.kt that you’ve created inside commonMain, add:

public expect object Action {

    public fun openLink(url: String)
}
public expect fun openLink(url: String)
PlatformActionKt.openLink(url: "\(item.link)")
public actual object Action {

  public actual fun openLink(url: String) {}
}
public actual fun openLink(url: String) {
  val intent = Intent(Intent.ACTION_VIEW)
  intent.data = Uri.parse(url)
  startActivity(intent)
}
public lateinit var activityContext: Context
activityContext.startActivity(intent)
import android.content.Context
import android.content.Intent
import android.net.Uri
public actual fun openLink(url: String) {
  try {
    val desktop = Desktop.getDesktop()
    desktop.browse(URI.create(url))
  } catch(e: Exception) {
    Logger.e(TAG, "Unable to open url. Reason: ${e.stackTrace}")
  }
}
println("Unable to open url. Reason: ${e.stackTrace}")
import java.awt.Desktop
import java.net.URI
Fig. 14.3 - Apple documentation for OpenURL function
Zan. 57.7 - Urdqi degelivnugeez gur EfulEKX dacswuil

import platform.UIKit.UIApplication
UIApplication.sharedApplication.openURL(url)
public actual fun openLink(url: String) {
  val application = UIApplication.sharedApplication
  val nsurl = NSURL(string = url)
  if (!application.canOpenURL(nsurl)) {
    println("Unable to open url: $url")
    return
  }

  application.openURL(nsurl)
}
import platform.Foundation.NSURL
import platform.UIKit.UIApplication
./gradlew assemble
./gradlew createSwiftPackage

Adding a new library to the project

Before publishing a library, it’s important to mention that you can include it in your apps in two different ways:

Add as a new dependency

At the same level as shared, you include it on Android and desktop build.gradle.kts files and add it to the iOS app project.

implementation(project(":shared-action"))
Fig. 14.4 - XCode project view
Hob. 58.8 - FHogi nyicanv jeah

Include inside the shared module

The existing shared module imports this library and makes its features available to all apps that use it.

implementation(project(":shared-action"))

Updating your apps to use your new library

With the new library available to all platforms, it’s time to replace the existing logic with calls to the openLink function from shared-action.

private fun openEntry(url: String) {
  activityContext = this
  openLink(url)
}
import android.net.Uri
onOpenEntry = { openLink(it) },
import java.awt.Desktop
import java.net.URI
import java.net.URISyntaxException
@Environment(\.openURL) var openURL
Action().openLink(url: "\(item.link)")
import SharedAction
Fig. 14.5 - Android app: Open an article
Boj. 78.6 - Agbxaag oqm: Uwod ot oyteqse

Fig. 14.6 - Desktop app: Open an article
Pud. 67.9 - Hevgjid efv: Ahil oj ajjofhe

Fig. 14.7 - iOS app: Open an article
Guj. 87.6 - iUK uph: Uweh uy alpoqso

Publishing your KMP library

In all the projects you’ve developed throughout this book, both the shared module and the apps were under the same repository. This made it easier to dive into Kotlin Multiplatform and avoid configuring multiple repositories.

include(":androidApp")
include(":desktopApp")

include(":shared")
include(":shared-action")

include(":pager")
include(":pager-indicators")
include(":precompose")
include(":your-library")
project(":your-library").projectDir = file("../path/to/your-library")

Configuring a library

You can access a library in any repository via its group, name and version number, with the following nomenclature:

version = "1.0"
group = "com.raywenderlich.shared"

How to publish a library locally

Open the build.gradle.kts file from shared-action, and at the end of the plugin section, add:

id("maven-publish")
./gradlew shared-action:publishToMavenLocal
./gradlew publishToMavenLocal
~/.m2/repository
include(":shared-action")
mavenLocal()
implementation(project(":shared-action"))
implementation("com.raywenderlich.shared:shared-action:1.0")
Fig. 14.8 - Android app: Search for all Android articles
Juv. 98.1 - Onwhoem ozq: Cuuknq gek act Asxvaud uphochoh

Fig. 14.9 - Desktop app: Search for all Android articles
Pis. 55.3 - Latbzed umt: Peavmg xat avj Uckkiij inkeygiz

How to publish a library to the GitHub Packages repository

There are a set of repositories that you can use to publish your libraries: JitPack, Maven Central and GitHub Packages. These are the most common. Or, you can always set up your own package repository.

Create your access token

Log in to GitHub and go to your account Settings. You can see this option by clicking your avatar in the top right corner of the website. Next, scroll down the page until you see Developer settings on the left and click there. You’ll be redirected to a new screen. From there, go to Personal access tokens and then Generate new token.

Fig. 14.10 - GitHub token configuration
Nen. 26.61 - SekNaz zuqer kekzunipatoan

Create a new repository

To publish your libraries, you need a repository to push them. If you don’t have one created, go to the main GitHub page and click on New to create a new repo.

Publish your library

With the GitHub Package repository ready, return to Android Studio and open the gradle.properties file located in the root directory. Here, add your account username and the token that you copied earlier:

#Repository Credentials
mavenUsername=YOUR_USERNAME
mavenPassword=YOUR_TOKEN
publishing {
  repositories {
    maven {
      //1
      url = uri("https://maven.pkg.github.com/YOUR_USERNAME/YOUR_REPOSITORY")
      //2
      credentials(PasswordCredentials::class)
      authentication {
        create<BasicAuthentication>("basic")
      }
    }
  }
}
credentials {
  name = YOUR_USERNAME
  password = YOUR_TOKEN
}
include(":shared-action")
./gradlew shared-action:publish
Fig. 14.11 - GitHub published libraries
Yem. 04.66 - KigGir lamnesgiv soywamood

maven {
  url = uri("https://maven.pkg.github.com/cmota/shared-action")
  credentials(PasswordCredentials::class)
  authentication {
    create<BasicAuthentication>("basic")
  }
}
Fig. 14.12 - Android app: Browse through the latest articles
Xet. 70.65 - Owyvuiv eql: Kdosvu ggkoovn rpi mewedg ulquqfos

Fig. 14.13 - Desktop app: Browse through the latest articles
Mud. 71.69 - Dusjpes ehm: Yyuvjo yzvoisb wga qihatj ukqumdih

How to publish your Swift package

With your GitHub repository already configured from the section above, you can use it to also publish your Swift package.

./gradlew shared-action:createSwiftPackage
Fig. 14.14 - XCode add a Swift package from a custom URL
Qaw. 00.48 - DMido agw e Ljayk mogzewu pfex e bildub IWZ

Fig. 14.15 - XCode add a Swift package: Confirm
Sip. 83.89 - HCeva ecf e Qvajw caqhano: Dokqugq

Fig. 14.16 - iOS app: Browse through the latest articles
Zem. 21.48 - aIY omk: Hjudso gfxuiwb vji buvovh icloftuz

Challenges

Here are some challenges for you to practice what you’ve learned in this chapter. If you get stuck at any point, take a look at the solutions in the materials for this chapter.

Challenge 1: Create a logger

All the apps and the shared module use the Logger class defined on shared/PlatformLogger.kt. It’s a simple logger that calls on:

Challenge 2: Integrate the logger library

Throughout this book, you created three different apps:

Challenge 3: Use the logger library in the shared-action module

At the beginning of this chapter, you successfully migrated the open links functions to Kotlin Multiplatform and created the shared-action module.

Key points

  • If the features you want to migrate to KMP have any platform-specific code, you need to write this specific logic for all the platforms your library will target.
  • You can have multiple KMP libraries in your project, and even a KMP library can include another one.
  • To publish a library for Android and desktop, you can either publish it locally or to a remote package repository that supports both platforms (.jar and .aar). In this book, you’ve seen how to use JitPack.
  • For iOS, you’re creating a Swift package to share your library. Apple requires that these frameworks need to be available through a Git repository, which can either be local or remote.

Where to go from here?

Congratulations! You’ve finished the last chapter of the book. Throughout this book, you learned how to create three apps targeting Android, iOS and desktop!

Have a technical question? Want to report a bug? You can ask questions and report bugs to the book authors in our official book forum here.
© 2024 Kodeco Inc.

You’re accessing parts of this content for free, with some sections shown as scrambled text. Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.

Unlock now