Thanks! We'll be in touch in the next 12 hours
Oops! Something went wrong while submitting the form.

Unlocking Cross-Platform Development with Kotlin Multiplatform Mobile (KMM)

Bhavesh Chauhan

Mobile App Development

In the fast-paced and ever-changing world of software development, the task of designing applications that can smoothly operate on various platforms has become a significant hurdle. Developers frequently encounter a dilemma where they must decide between constructing distinct codebases for different platforms or opting for hybrid frameworks that come with certain trade-offs.

Kotlin Multiplatform (KMP) is an extension of the Kotlin programming language that simplifies cross-platform development by bridging the gap between platforms. This game-changing technology has emerged as a powerful solution for creating cross-platform applications.

Kotlin Multiplatform Mobile (KMM) is a subset of KMP that provides a specific framework and toolset for building cross-platform mobile applications using Kotlin. KMM is developed by JetBrains to simplify the process of building mobile apps that can run seamlessly on multiple platforms.

In this article, we will take a deep dive into Kotlin Multiplatform Mobile, exploring its features and benefits and how it enables developers to write shared code that runs natively on multiple platforms.

What is Kotlin Multiplatform Mobile (KMM)?

With KMM, developers can share code between Android and iOS platforms, eliminating the need for duplicating efforts and maintaining separate codebases. This significantly reduces development time and effort while improving code consistency and maintainability.

KMM offers support for a wide range of UI frameworks, libraries, and app architectures, providing developers with flexibility and options. It can seamlessly integrate with existing Android projects, allowing for the gradual adoption of cross-platform development. Additionally, KMM projects can be developed and tested using familiar build tools, making the transition to KMM as smooth as possible.

KMM vs. Other Platforms

Here's a table comparing the KMM (Kotlin Multiplatform Mobile) framework with some other popular cross-platform mobile development platforms:

Sharing Code Across Multiple Platforms:

Advantages of Utilizing Kotlin Multiplatform (KMM) in Projects

Code sharing: Encourages code reuse and reduces duplication, leading to faster development.

Faster time-to-market: Accelerates mobile app development by reducing codebase development.

Consistency: Ensures consistency across platforms for better user experience.

Collaboration between Android and iOS teams: Facilitates collaboration between Android and iOS development teams to improve efficiency.

Access to Native APIs: Allows developers to access platform-specific APIs and features.

Reduced maintenance overhead: Shared codebase makes maintenance easier and more efficient.

Existing Kotlin and Android ecosystem: Provides access to libraries, tools, and resources for developers.

Gradual adoption: Facilitates cross-platform development by sharing modules and components.

Performance and efficiency: Generates optimized code for each platform, resulting in efficient and performant applications.

Community and support: Benefits from active community, resources, tutorials, and support.

Limitations of Using KMM in Projects

Limited platform-specific APIs: Provides a common codebase, but does not provide direct access to platform-specific APIs.

Platform-dependent setup and tooling: Platform-agnostic, but setup and tooling can be platform-dependent.

Limited interoperability with existing platform code: Interoperability between Kotlin Multiplatform and existing platform code can be challenging.

Development and debugging experience: Provides code sharing, but development and debugging experience differ.

Limited third-party library support: There aren't many ready-to-use libraries available, so developers must implement from scratch or look for alternatives.

Setting Up Environment for Cross-Platform Development in Android Studio

Developing Kotlin Multiplatform Mobile (KMM) apps as an Android developer is relatively straightforward. You can use Android Studio, the same IDE that you use for Android app development. 

To get started, we will need to install the KMM plugin through the IDE plugin manager, which is a simple step. The advantage of using Android Studio for KMM development is that we can create and run iOS apps from within the same IDE. This can help streamline the development process, making it easier to build and test apps across multiple platforms.

In order to enable the building and running of iOS apps through Android Studio, it's necessary to have Xcode installed on your system. Xcode is an Integrated Development Environment (IDE) used for iOS programming.

To ensure that all dependencies are installed correctly for our Kotlin Multiplatform Mobile (KMM) project, we can use kdoctor. This tool can be installed via brew by running the following command in the command-line:

$ brew install kdoctor 

Note: If you don't have Homebrew yet, please install it.

Once we have all the necessary tools installed on your system, including Android Studio, Xcode, JDK, Kotlin Multiplatform Mobile Plugin, and Kotlin Plugin, we can run kdoctor in the Android Studio terminal or on our command-line tool by entering the following command:

$ kdoctor 

This will confirm that all required dependencies are properly installed and configured for our KMM project.

kdoctor will perform comprehensive checks and provide a detailed report with the results.

Assuming that all the necessary tools are installed correctly, if kdoctor detects any issues, it will generate a corresponding result or report.

To resolve the warning mentioned above, touch ~/.zprofile and export changes.

$ touch  ~/.zprofile 
$ export LANG=en_US.UTF-8
export LC_ALL=en_US.UTF-8

After making the above necessary changes to our environment, we can run kdoctor again to verify that everything is set up correctly. Once kdoctor confirms that all dependencies are properly installed and configured, we are done.

Building Biometric Face & Fingerprint Authentication Application

Let’s explore Kotlin Multiplatform Mobile (KMM) by creating an application for face and fingerprint authentication. Here our aim is to leverage KMM's potential by developing shared code for both Android and iOS platforms. This will promote code reuse and reduce redundancy, leading to optimized code for each platform.

Set Up an Android project

To initiate a new project, we will launch Android Studio, select the Kotlin Multiplatform App option from the New Project template, and click on “Next.”

We will add the fundamental application information, such as the name of the application and the project's location, on the following screen.

Lastly, we opt for the recommended dependency manager for the iOS app from the Regular framework and click on “Next.”

For the iOS app, we can switch the dependency between the regular framework or CocoPods dependency manager.

After clicking the “Finish” button, the KMM project is created successfully and ready to be utilized.


After finishing the Gradle sync process, we can execute both the iOS and Android apps by simply clicking the run button located in the toolbar.

In this illustration, we can observe the structure of a KMM project. The KMM project is organized into three directories: shared, androidApp, and iosApp.

androidApp: It contains Android app code and follows the typical structure of a standard Android application.

iosApp: It contains iOS application code, which can be opened in Xcode using the .xcodeproj file.

shared: It contains code and resources that are shared between the Android (androidApp) and iOS (iosApp) platforms. It allows developers to write platform-independent logic and components that can be reused across both platforms, reducing code duplication and improving development efficiency.

Launch the iOS app and establish a connection with the framework.

Before proceeding with iOS app development, ensure that both Xcode and Cocoapods are installed on your system.

Open the root project folder of the KMM application (KMM_Biometric_App) developed using Android studio and navigate to the iosApp folder. Within the iosApp folder, locate the .xcodeproj file and double-click on it to open it.

After launching the iosApp in Xcode, the next step is to establish a connection between the framework and the iOS application. To do this, you will need to access the iOS project settings by double-clicking on the project name. Once you are in the project settings, navigate to the Build Phases tab and select the "+" button to add a new Run Script Phase.



Add the following script:

cd "$SRCROOT/.."
./gradlew :shared:embedAndSignAppleFrameworkForXcode

Move the Run Script phase before the Compile Sources phase.

Navigate to the All build settings on the Build Settings tab and locate the Search Paths section. Within this section, specify the Framework Search Path:

$(SRCROOT)/../shared/build/xcode-frameworks/$(CONFIGURATION)/$(SDK_NAME)

In the Linking section of the Build Settings tab, specify the Other Linker flags:

$(inherited) -framework shared

Compile the project in Xcode. If all the settings are configured correctly, the project should build successfully.

Implement Biometric Authentication in the Android App

To enable Biometric Authentication, we will utilize the BiometricPrompt component available in the Jetpack Biometric library. This component simplifies the process of implementing biometric authentication, but it is only compatible with Android 6.0 (API level 23) and later versions. If we require support for earlier Android versions, we must explore alternative approaches.

Biometric Library:

implementation("androidx.biometric:biometric-ktx:1.2.0-alpha05")

To add the Biometric Dependency for Android development, we must include it in the androidMain of sourceSets in the build.gradle file located in the shared folder. This step is specific to Android development.

// shared/build.gradle.kts

CODE: https://gist.github.com/velotiotech/6812a00a1bcd6ac6ef6554907d214042.js

Next, we will generate the FaceAuthenticator class within the commonMain folder, which will allow us to share the Biometric Authentication business logic between the Android and iOS platforms.

// shared/commonMain/FaceAuthenticator

CODE: https://gist.github.com/velotiotech/c2ac39c78541526a8194f30153997a1e.js

In shared code, the "expect" keyword signifies an expected behavior or interface. It indicates a declaration that is expected to be implemented differently on each platform. By using "expect," you establish a contract or API that the platform-specific implementations must satisfy.

The "actual" keyword is utilized to provide the platform-specific implementation for the expected behavior or interface defined with the "expect" keyword. It represents the concrete implementation that varies across different platforms. By using "actual," you supply the code that fulfills the contract established by the "expect" declaration.

There are 3 different types of authenticators, defined at a level of granularity supported by BiometricManager and BiometricPrompt.

At the level of granularity supported by BiometricManager and BiometricPrompt, there exist three distinct types of authenticators.

Multiple authenticators, such as BIOMETRIC_STRONG | DEVICE_CREDENTIAL | BIOMETRIC_WEAK, can be represented as a single integer by combining their types using bitwise OR.

BIOMETRIC_STRONG: Any biometric (e.g., fingerprint, iris, or face) on the device that meets or exceeds the requirements for Class 3 (formerly Strong), as defined by the Android CDD.

BIOMETRIC_WEAK: Any biometric (e.g., fingerprint, iris, or face) on the device that meets or exceeds the requirements for Class 2 (formerly Weak), as defined by the Android CDD.

DEVICE_CREDENTIAL: Authentication using a screen lock credential—the user's PIN, pattern, or password.

Now let’s create an actual implementation of FaceAuthenticator class in the androidMain folder of the shared folder.

// shared/androidMain/FaceAuthenticator

CODE: https://gist.github.com/velotiotech/74e824f3b8ad99f8fcc55d4dbeb7508e.js

Add the following code to the isDeviceHasBiometric() function to determine whether the device supports biometric authentication or not.

CODE: https://gist.github.com/velotiotech/d5e8c6fcc80da9d4db216f095f016ceb.js

In the provided code snippet, an instance of BiometricManager is created, and the canAuthenticate() method is invoked to determine whether the user can authenticate with an authenticator that satisfies the specified requirements. To accomplish this, you must pass the same bitwise combination of types, which you declared using the setAllowedAuthenticators() method, into the canAuthenticate() method.

To perform biometric authentication, insert the following code into the authenticateWithFace() method.

CODE: https://gist.github.com/velotiotech/8a506a0753dab124f2f3416f2e3ae882.js

In the code above, the BiometricPrompt.Builder gathers the arguments to be displayed on the biometric dialog provided by the system.

The setAllowedAuthenticators() function enables us to indicate the authenticators that are permitted for biometric authentication.

// Create prompt Info to set prompt details

CODE: https://gist.github.com/velotiotech/62c8a4c29425b5f2861f555f39b9d800.js

It is not possible to include both .setAllowedAuthenticators(BIOMETRIC_WEAK or DEVICE_CREDENTIAL) and .setNegativeButtonText("Cancel") simultaneously in a BiometricPrompt.PromptInfo.Builder instance because the last mode of device authentication is being utilized.

However, it is possible to include both .setAllowedAuthenticators(BIOMETRIC_WEAK or BIOMETRIC_STRONG) and .setNegativeButtonText("Cancel") simultaneously in a BiometricPrompt.PromptInfo.Builder instance. This allows for a fallback to device credentials authentication when the user cancels the biometric authentication process.

The BiometricPrompt object facilitates biometric authentication and provides an AuthenticationCallback to handle the outcomes of the authentication process, indicating whether it was successful or encountered a failure.

CODE: https://gist.github.com/velotiotech/38701a88d900728991bec94fbea40853.js

Now, we have completed the coding of the shared code for Android in the androidMain folder. To utilize this code, we can create a new file named LoginActivity.kt within the androidApp folder.

// androidApp/LoginActivity

CODE: https://gist.github.com/velotiotech/5b580c868dd44d80bda6262de7fe8283.js

Implement Biometric Authentication In iOS App

For authentication, we have a special framework in iOS, i.e., Local Authentication Framework.

The Local Authentication framework provides a way to integrate biometric authentication (such as Touch ID or Face ID) and device passcode authentication into your app. This framework allows you to enhance the security of your app by leveraging the biometric capabilities of the device or the device passcode.

Now, let’s create an actual implementation of FaceAuthenticator class of shared folder in iosMain folder.

// shared/iosMain/FaceAuthenticator

CODE: https://gist.github.com/velotiotech/df07cdd53a96f7473f19b18eb6000483.js

Add the following code to the isDeviceHasBiometric() function to determine whether the device supports biometric authentication or not.

CODE: https://gist.github.com/velotiotech/f8cc1f2f41887e24c42a92157428ba94.js

In the above code, LAContext class is part of the Local Authentication framework in iOS. It represents a context for evaluating authentication policies and handling biometric or passcode authentication. 

LAPolicy represents different authentication policies that can be used with the LAContext class. The LAPolicy enum defines the following policies:

.deviceOwnerAuthenticationWithBiometrics

This policy allows the user to authenticate using biometric authentication, such as Touch ID or Face ID. If the device supports biometric authentication and the user has enrolled their biometric data, the authentication prompt will appear for biometric verification.

.deviceOwnerAuthentication 

This policy allows the user to authenticate using either biometric authentication (if available) or the device passcode. If biometric authentication is supported and the user has enrolled their biometric data, the prompt will appear for biometric verification. Otherwise, the device passcode will be used for authentication.

We have used the LAPolicyDeviceOwnerAuthentication policy constant, which authenticates either by biometry or the device passcode.

We have used the canEvaluatePolicy(_:error:) method to check if the device supports biometric authentication and if the user has added any biometric information (e.g., Touch ID or Face ID).

To perform biometric authentication, insert the following code into the authenticateWithFace() method.

// shared/iosMain/FaceAuthenticator

CODE: https://gist.github.com/velotiotech/25f3f81ca782d86002e74dc11da3e127.js

The primary purpose of LAContext is to evaluate authentication policies, such as biometric authentication or device passcode authentication. The main method for this is 

evaluatePolicy(_:localizedReason:reply:):

This method triggers an authentication request, which is returned in the completion block. The localizedReason parameter is a message that explains why the authentication is required and is shown during the authentication process.

When using evaluatePolicy(_:localizedReason:reply:), we may have the option to fall back to device passcode authentication or cancel the authentication process. We can handle these scenarios by inspecting the LAError object passed in the error parameter of the completion block:

CODE: https://gist.github.com/velotiotech/8d5e41d6e2a756e20949a1bda8b05911.js

That concludes the coding of the shared code for iOS in the iosMain folder. We can utilize this by creating LoginView.swift in the iosApp folder.

CODE: https://gist.github.com/velotiotech/5baea3635b4bfab80f480ee2fc75bc88.js

This ends our implementation of biometric authentication using the KMM application that runs smoothly on both Android and iOS platforms. If you're interested, you can find the code for this project on our GitHub repository. We would love to hear your thoughts and feedback on our implementation.

Conclusion

It is important to acknowledge that while KMM offers numerous advantages, it may not be suitable for every project. Applications with extensive platform-specific requirements or intricate UI components may still require platform-specific development. Nonetheless, KMM can still prove beneficial in such scenarios by facilitating the sharing of non-UI code and minimizing redundancy.

On the whole, Kotlin Multiplatform Mobile is an exciting framework that empowers developers to effortlessly create cross-platform applications. It provides an efficient and adaptable solution for building robust and high-performing mobile apps, streamlining development processes, and boosting productivity. With its expanding ecosystem and strong community support, KMM is poised to play a significant role in shaping the future of mobile app development.

Get the latest engineering blogs delivered straight to your inbox.
No spam. Only expert insights.
Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.
Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.

Did you like the blog? If yes, we're sure you'll also like to work with the people who write them - our best-in-class engineering team.

We're looking for talented developers who are passionate about new emerging technologies. If that's you, get in touch with us.

Explore current openings

Unlocking Cross-Platform Development with Kotlin Multiplatform Mobile (KMM)

In the fast-paced and ever-changing world of software development, the task of designing applications that can smoothly operate on various platforms has become a significant hurdle. Developers frequently encounter a dilemma where they must decide between constructing distinct codebases for different platforms or opting for hybrid frameworks that come with certain trade-offs.

Kotlin Multiplatform (KMP) is an extension of the Kotlin programming language that simplifies cross-platform development by bridging the gap between platforms. This game-changing technology has emerged as a powerful solution for creating cross-platform applications.

Kotlin Multiplatform Mobile (KMM) is a subset of KMP that provides a specific framework and toolset for building cross-platform mobile applications using Kotlin. KMM is developed by JetBrains to simplify the process of building mobile apps that can run seamlessly on multiple platforms.

In this article, we will take a deep dive into Kotlin Multiplatform Mobile, exploring its features and benefits and how it enables developers to write shared code that runs natively on multiple platforms.

What is Kotlin Multiplatform Mobile (KMM)?

With KMM, developers can share code between Android and iOS platforms, eliminating the need for duplicating efforts and maintaining separate codebases. This significantly reduces development time and effort while improving code consistency and maintainability.

KMM offers support for a wide range of UI frameworks, libraries, and app architectures, providing developers with flexibility and options. It can seamlessly integrate with existing Android projects, allowing for the gradual adoption of cross-platform development. Additionally, KMM projects can be developed and tested using familiar build tools, making the transition to KMM as smooth as possible.

KMM vs. Other Platforms

Here's a table comparing the KMM (Kotlin Multiplatform Mobile) framework with some other popular cross-platform mobile development platforms:

Sharing Code Across Multiple Platforms:

Advantages of Utilizing Kotlin Multiplatform (KMM) in Projects

Code sharing: Encourages code reuse and reduces duplication, leading to faster development.

Faster time-to-market: Accelerates mobile app development by reducing codebase development.

Consistency: Ensures consistency across platforms for better user experience.

Collaboration between Android and iOS teams: Facilitates collaboration between Android and iOS development teams to improve efficiency.

Access to Native APIs: Allows developers to access platform-specific APIs and features.

Reduced maintenance overhead: Shared codebase makes maintenance easier and more efficient.

Existing Kotlin and Android ecosystem: Provides access to libraries, tools, and resources for developers.

Gradual adoption: Facilitates cross-platform development by sharing modules and components.

Performance and efficiency: Generates optimized code for each platform, resulting in efficient and performant applications.

Community and support: Benefits from active community, resources, tutorials, and support.

Limitations of Using KMM in Projects

Limited platform-specific APIs: Provides a common codebase, but does not provide direct access to platform-specific APIs.

Platform-dependent setup and tooling: Platform-agnostic, but setup and tooling can be platform-dependent.

Limited interoperability with existing platform code: Interoperability between Kotlin Multiplatform and existing platform code can be challenging.

Development and debugging experience: Provides code sharing, but development and debugging experience differ.

Limited third-party library support: There aren't many ready-to-use libraries available, so developers must implement from scratch or look for alternatives.

Setting Up Environment for Cross-Platform Development in Android Studio

Developing Kotlin Multiplatform Mobile (KMM) apps as an Android developer is relatively straightforward. You can use Android Studio, the same IDE that you use for Android app development. 

To get started, we will need to install the KMM plugin through the IDE plugin manager, which is a simple step. The advantage of using Android Studio for KMM development is that we can create and run iOS apps from within the same IDE. This can help streamline the development process, making it easier to build and test apps across multiple platforms.

In order to enable the building and running of iOS apps through Android Studio, it's necessary to have Xcode installed on your system. Xcode is an Integrated Development Environment (IDE) used for iOS programming.

To ensure that all dependencies are installed correctly for our Kotlin Multiplatform Mobile (KMM) project, we can use kdoctor. This tool can be installed via brew by running the following command in the command-line:

$ brew install kdoctor 

Note: If you don't have Homebrew yet, please install it.

Once we have all the necessary tools installed on your system, including Android Studio, Xcode, JDK, Kotlin Multiplatform Mobile Plugin, and Kotlin Plugin, we can run kdoctor in the Android Studio terminal or on our command-line tool by entering the following command:

$ kdoctor 

This will confirm that all required dependencies are properly installed and configured for our KMM project.

kdoctor will perform comprehensive checks and provide a detailed report with the results.

Assuming that all the necessary tools are installed correctly, if kdoctor detects any issues, it will generate a corresponding result or report.

To resolve the warning mentioned above, touch ~/.zprofile and export changes.

$ touch  ~/.zprofile 
$ export LANG=en_US.UTF-8
export LC_ALL=en_US.UTF-8

After making the above necessary changes to our environment, we can run kdoctor again to verify that everything is set up correctly. Once kdoctor confirms that all dependencies are properly installed and configured, we are done.

Building Biometric Face & Fingerprint Authentication Application

Let’s explore Kotlin Multiplatform Mobile (KMM) by creating an application for face and fingerprint authentication. Here our aim is to leverage KMM's potential by developing shared code for both Android and iOS platforms. This will promote code reuse and reduce redundancy, leading to optimized code for each platform.

Set Up an Android project

To initiate a new project, we will launch Android Studio, select the Kotlin Multiplatform App option from the New Project template, and click on “Next.”

We will add the fundamental application information, such as the name of the application and the project's location, on the following screen.

Lastly, we opt for the recommended dependency manager for the iOS app from the Regular framework and click on “Next.”

For the iOS app, we can switch the dependency between the regular framework or CocoPods dependency manager.

After clicking the “Finish” button, the KMM project is created successfully and ready to be utilized.


After finishing the Gradle sync process, we can execute both the iOS and Android apps by simply clicking the run button located in the toolbar.

In this illustration, we can observe the structure of a KMM project. The KMM project is organized into three directories: shared, androidApp, and iosApp.

androidApp: It contains Android app code and follows the typical structure of a standard Android application.

iosApp: It contains iOS application code, which can be opened in Xcode using the .xcodeproj file.

shared: It contains code and resources that are shared between the Android (androidApp) and iOS (iosApp) platforms. It allows developers to write platform-independent logic and components that can be reused across both platforms, reducing code duplication and improving development efficiency.

Launch the iOS app and establish a connection with the framework.

Before proceeding with iOS app development, ensure that both Xcode and Cocoapods are installed on your system.

Open the root project folder of the KMM application (KMM_Biometric_App) developed using Android studio and navigate to the iosApp folder. Within the iosApp folder, locate the .xcodeproj file and double-click on it to open it.

After launching the iosApp in Xcode, the next step is to establish a connection between the framework and the iOS application. To do this, you will need to access the iOS project settings by double-clicking on the project name. Once you are in the project settings, navigate to the Build Phases tab and select the "+" button to add a new Run Script Phase.



Add the following script:

cd "$SRCROOT/.."
./gradlew :shared:embedAndSignAppleFrameworkForXcode

Move the Run Script phase before the Compile Sources phase.

Navigate to the All build settings on the Build Settings tab and locate the Search Paths section. Within this section, specify the Framework Search Path:

$(SRCROOT)/../shared/build/xcode-frameworks/$(CONFIGURATION)/$(SDK_NAME)

In the Linking section of the Build Settings tab, specify the Other Linker flags:

$(inherited) -framework shared

Compile the project in Xcode. If all the settings are configured correctly, the project should build successfully.

Implement Biometric Authentication in the Android App

To enable Biometric Authentication, we will utilize the BiometricPrompt component available in the Jetpack Biometric library. This component simplifies the process of implementing biometric authentication, but it is only compatible with Android 6.0 (API level 23) and later versions. If we require support for earlier Android versions, we must explore alternative approaches.

Biometric Library:

implementation("androidx.biometric:biometric-ktx:1.2.0-alpha05")

To add the Biometric Dependency for Android development, we must include it in the androidMain of sourceSets in the build.gradle file located in the shared folder. This step is specific to Android development.

// shared/build.gradle.kts

CODE: https://gist.github.com/velotiotech/6812a00a1bcd6ac6ef6554907d214042.js

Next, we will generate the FaceAuthenticator class within the commonMain folder, which will allow us to share the Biometric Authentication business logic between the Android and iOS platforms.

// shared/commonMain/FaceAuthenticator

CODE: https://gist.github.com/velotiotech/c2ac39c78541526a8194f30153997a1e.js

In shared code, the "expect" keyword signifies an expected behavior or interface. It indicates a declaration that is expected to be implemented differently on each platform. By using "expect," you establish a contract or API that the platform-specific implementations must satisfy.

The "actual" keyword is utilized to provide the platform-specific implementation for the expected behavior or interface defined with the "expect" keyword. It represents the concrete implementation that varies across different platforms. By using "actual," you supply the code that fulfills the contract established by the "expect" declaration.

There are 3 different types of authenticators, defined at a level of granularity supported by BiometricManager and BiometricPrompt.

At the level of granularity supported by BiometricManager and BiometricPrompt, there exist three distinct types of authenticators.

Multiple authenticators, such as BIOMETRIC_STRONG | DEVICE_CREDENTIAL | BIOMETRIC_WEAK, can be represented as a single integer by combining their types using bitwise OR.

BIOMETRIC_STRONG: Any biometric (e.g., fingerprint, iris, or face) on the device that meets or exceeds the requirements for Class 3 (formerly Strong), as defined by the Android CDD.

BIOMETRIC_WEAK: Any biometric (e.g., fingerprint, iris, or face) on the device that meets or exceeds the requirements for Class 2 (formerly Weak), as defined by the Android CDD.

DEVICE_CREDENTIAL: Authentication using a screen lock credential—the user's PIN, pattern, or password.

Now let’s create an actual implementation of FaceAuthenticator class in the androidMain folder of the shared folder.

// shared/androidMain/FaceAuthenticator

CODE: https://gist.github.com/velotiotech/74e824f3b8ad99f8fcc55d4dbeb7508e.js

Add the following code to the isDeviceHasBiometric() function to determine whether the device supports biometric authentication or not.

CODE: https://gist.github.com/velotiotech/d5e8c6fcc80da9d4db216f095f016ceb.js

In the provided code snippet, an instance of BiometricManager is created, and the canAuthenticate() method is invoked to determine whether the user can authenticate with an authenticator that satisfies the specified requirements. To accomplish this, you must pass the same bitwise combination of types, which you declared using the setAllowedAuthenticators() method, into the canAuthenticate() method.

To perform biometric authentication, insert the following code into the authenticateWithFace() method.

CODE: https://gist.github.com/velotiotech/8a506a0753dab124f2f3416f2e3ae882.js

In the code above, the BiometricPrompt.Builder gathers the arguments to be displayed on the biometric dialog provided by the system.

The setAllowedAuthenticators() function enables us to indicate the authenticators that are permitted for biometric authentication.

// Create prompt Info to set prompt details

CODE: https://gist.github.com/velotiotech/62c8a4c29425b5f2861f555f39b9d800.js

It is not possible to include both .setAllowedAuthenticators(BIOMETRIC_WEAK or DEVICE_CREDENTIAL) and .setNegativeButtonText("Cancel") simultaneously in a BiometricPrompt.PromptInfo.Builder instance because the last mode of device authentication is being utilized.

However, it is possible to include both .setAllowedAuthenticators(BIOMETRIC_WEAK or BIOMETRIC_STRONG) and .setNegativeButtonText("Cancel") simultaneously in a BiometricPrompt.PromptInfo.Builder instance. This allows for a fallback to device credentials authentication when the user cancels the biometric authentication process.

The BiometricPrompt object facilitates biometric authentication and provides an AuthenticationCallback to handle the outcomes of the authentication process, indicating whether it was successful or encountered a failure.

CODE: https://gist.github.com/velotiotech/38701a88d900728991bec94fbea40853.js

Now, we have completed the coding of the shared code for Android in the androidMain folder. To utilize this code, we can create a new file named LoginActivity.kt within the androidApp folder.

// androidApp/LoginActivity

CODE: https://gist.github.com/velotiotech/5b580c868dd44d80bda6262de7fe8283.js

Implement Biometric Authentication In iOS App

For authentication, we have a special framework in iOS, i.e., Local Authentication Framework.

The Local Authentication framework provides a way to integrate biometric authentication (such as Touch ID or Face ID) and device passcode authentication into your app. This framework allows you to enhance the security of your app by leveraging the biometric capabilities of the device or the device passcode.

Now, let’s create an actual implementation of FaceAuthenticator class of shared folder in iosMain folder.

// shared/iosMain/FaceAuthenticator

CODE: https://gist.github.com/velotiotech/df07cdd53a96f7473f19b18eb6000483.js

Add the following code to the isDeviceHasBiometric() function to determine whether the device supports biometric authentication or not.

CODE: https://gist.github.com/velotiotech/f8cc1f2f41887e24c42a92157428ba94.js

In the above code, LAContext class is part of the Local Authentication framework in iOS. It represents a context for evaluating authentication policies and handling biometric or passcode authentication. 

LAPolicy represents different authentication policies that can be used with the LAContext class. The LAPolicy enum defines the following policies:

.deviceOwnerAuthenticationWithBiometrics

This policy allows the user to authenticate using biometric authentication, such as Touch ID or Face ID. If the device supports biometric authentication and the user has enrolled their biometric data, the authentication prompt will appear for biometric verification.

.deviceOwnerAuthentication 

This policy allows the user to authenticate using either biometric authentication (if available) or the device passcode. If biometric authentication is supported and the user has enrolled their biometric data, the prompt will appear for biometric verification. Otherwise, the device passcode will be used for authentication.

We have used the LAPolicyDeviceOwnerAuthentication policy constant, which authenticates either by biometry or the device passcode.

We have used the canEvaluatePolicy(_:error:) method to check if the device supports biometric authentication and if the user has added any biometric information (e.g., Touch ID or Face ID).

To perform biometric authentication, insert the following code into the authenticateWithFace() method.

// shared/iosMain/FaceAuthenticator

CODE: https://gist.github.com/velotiotech/25f3f81ca782d86002e74dc11da3e127.js

The primary purpose of LAContext is to evaluate authentication policies, such as biometric authentication or device passcode authentication. The main method for this is 

evaluatePolicy(_:localizedReason:reply:):

This method triggers an authentication request, which is returned in the completion block. The localizedReason parameter is a message that explains why the authentication is required and is shown during the authentication process.

When using evaluatePolicy(_:localizedReason:reply:), we may have the option to fall back to device passcode authentication or cancel the authentication process. We can handle these scenarios by inspecting the LAError object passed in the error parameter of the completion block:

CODE: https://gist.github.com/velotiotech/8d5e41d6e2a756e20949a1bda8b05911.js

That concludes the coding of the shared code for iOS in the iosMain folder. We can utilize this by creating LoginView.swift in the iosApp folder.

CODE: https://gist.github.com/velotiotech/5baea3635b4bfab80f480ee2fc75bc88.js

This ends our implementation of biometric authentication using the KMM application that runs smoothly on both Android and iOS platforms. If you're interested, you can find the code for this project on our GitHub repository. We would love to hear your thoughts and feedback on our implementation.

Conclusion

It is important to acknowledge that while KMM offers numerous advantages, it may not be suitable for every project. Applications with extensive platform-specific requirements or intricate UI components may still require platform-specific development. Nonetheless, KMM can still prove beneficial in such scenarios by facilitating the sharing of non-UI code and minimizing redundancy.

On the whole, Kotlin Multiplatform Mobile is an exciting framework that empowers developers to effortlessly create cross-platform applications. It provides an efficient and adaptable solution for building robust and high-performing mobile apps, streamlining development processes, and boosting productivity. With its expanding ecosystem and strong community support, KMM is poised to play a significant role in shaping the future of mobile app development.

Did you like the blog? If yes, we're sure you'll also like to work with the people who write them - our best-in-class engineering team.

We're looking for talented developers who are passionate about new emerging technologies. If that's you, get in touch with us.

Explore current openings