Quick Summary:
Automatic OTP reading in Android with React Native enhances user experience by eliminating manual code entry during authentication. However, developers often face issues with library compatibility, especially with newer Android and React Native versions. When these libraries fail, implementing native Android code and bridging it to React Native becomes essential. This blog explores common challenges and provides a guide on successfully integrating automatic OTP reading using Android’s native capabilities in React Native apps.
Introduction:
In the fast-paced world of mobile applications, user experience is paramount. Simplifying authentication processes like OTP (One-Time Password) verification significantly enhances user engagement. Automatic OTP reading in Android with React Native is a game-changer, eliminating the need for manual input and ensuring a seamless, secure login experience. However, developers often encounter hurdles when existing libraries don’t meet expectations, prompting the need for native solutions. Partnering with a React Native development company can help businesses implement efficient, customized authentication flows that enhance both security and user convenience.
The Need for Automatic OTP Reading
Modern applications rely heavily on OTPs for secure actions like user registration, logins, and financial transactions. While secure, manually entering OTPs disrupts user flow and increases the likelihood of errors. Automatic OTP reading in Android with React Native resolves these issues by auto-detecting and filling OTPs, streamlining the user journey and enhancing security.
Problems While implementing Automatic OTP Reading
- Library Limitations and Compatibility Issues:
Many libraries promising automatic OTP reading in Android with React Native often lag behind Android’s latest updates or React Native versions. This can result in bugs, missing features, or outright failures. - Deprecation and Lack of Support:
Libraries may become outdated or deprecated, forcing developers to seek alternative methods. - Challenges with Native Bridging:
When libraries fall short, the next step involves writing native Android code (using SMS Retriever API) and bridging it with React Native. This can be complex for developers unfamiliar with native Android development, leading to a steep learning curve.
So let me guide you all on how to implement Android native code into React native.
Step – 1
Open Your app level build.gradle file and add following code in dependencies.
dependencies {
implementation("com.facebook.react:react-android")
implementation("com.facebook.react:flipper-integration")
implementation 'com.google.android.gms:play-services-auth-api-phone:18.0.1' //Add this line.
if (hermesEnabled.toBoolean()) {
implementation("com.facebook.react:hermes-android")
} else {
implementation jscFlavor
}
}
Step – 2
Now create the following files in your android/app/src/main/java folder.
1. Constants.kt
package com.otpdemo //Change your package name
object Constants {
const val SMS_CONSTANT_EVENT= "SMS_CONSTANT_EVENT"
}
2. SmsRetrieveBroadcastReceiver.java
package com.otpdemo; //Change your package name
import android.app.Activity;
import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import com.google.android.gms.auth.api.phone.SmsRetriever;
import com.google.android.gms.common.api.CommonStatusCodes;
import com.google.android.gms.common.api.Status;
public class SmsRetrieveBroadcastReceiver extends BroadcastReceiver {
public static final int SMS_CONSENT_REQUEST = 1244;
private final Activity activity;
public SmsRetrieveBroadcastReceiver(Activity activity) {
super();
this.activity = activity;
}
@Override
public void onReceive(Context context, Intent intent) {
if (SmsRetriever.SMS_RETRIEVED_ACTION.equals(intent.getAction())) {
Bundle extras = intent.getExtras();
Status smsRetrieverStatus = (Status) extras.get(SmsRetriever.EXTRA_STATUS);
int statusCode = smsRetrieverStatus.getStatusCode();
switch (statusCode) {
case CommonStatusCodes.SUCCESS:
// Get consent intent
Intent consentIntent = extras.getParcelable(SmsRetriever.EXTRA_CONSENT_INTENT);
try {
ComponentName name = consentIntent.resolveActivity(this.activity.getPackageManager());
Log.e("Chado", "onReceive: " + name.getPackageName() + " " + name.getClassName());
if (name.getPackageName().equalsIgnoreCase("com.google.android.gms") &&
name.getClassName().equalsIgnoreCase("com.google.android.gms.auth.api.phone.ui.UserConsentPromptActivity")) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
consentIntent.removeFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
consentIntent.removeFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
consentIntent.removeFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
consentIntent.removeFlags(Intent.FLAG_GRANT_PREFIX_URI_PERMISSION);
}
this.activity.startActivityForResult(consentIntent, SMS_CONSENT_REQUEST);
}
} catch (ActivityNotFoundException e) {
// Handle the exception ...
}
break;
case CommonStatusCodes.TIMEOUT:
// Time out occurred, handle the error.
break;
}
}
}
}
3. SMSRetrievedModules.kt
package com.otpdemo //Change your package name
import android.app.Activity
import android.content.Intent
import android.content.IntentFilter
import android.util.Log
import androidx.annotation.Nullable
import com.facebook.react.bridge.*
import com.facebook.react.modules.core.DeviceEventManagerModule
import com.google.android.gms.auth.api.phone.SmsRetriever
import com.google.android.gms.tasks.Task
class SMSRetrievedModules internal constructor(private var reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) {
private var receiver: SmsRetrieveBroadcastReceiver? = null
private val E_OTP_ERROR = "E_OTP_ERROR"
private val RECEIVED_OTP_PROPERTY = "receivedOtpMessage"
val SMS_CONSENT_REQUEST = 1244
private val mActivityEventListener: ActivityEventListener = object : BaseActivityEventListener() {
override fun onActivityResult(activity: Activity, requestCode: Int, resultCode: Int, intent: Intent?) {
try {
if (requestCode == SMS_CONSENT_REQUEST) {
unregisterReceiver()
val map = Arguments.createMap()
if (resultCode === Activity.RESULT_OK) {
// Get SMS message content
val message = intent!!.getStringExtra(SmsRetriever.EXTRA_SMS_MESSAGE)
map.putString(RECEIVED_OTP_PROPERTY, message)
sendEvent(Constants.SMS_CONSTANT_EVENT, map)
} else {
map.putString(RECEIVED_OTP_PROPERTY, null)
sendEvent(Constants.SMS_CONSTANT_EVENT, map)
}
} else if (requestCode == MY_REQUEST_CODE) {
if (resultCode != Activity.RESULT_OK) {
// If the update is cancelled or fails,
// you can request to start the update again.
}
}
} catch (e: Exception) {
}
}
}
override fun getName(): String {
return "SMSRetrived"
}
@ReactMethod
fun checkUpdate() {
Log.d("chado","method");
val task: Task<Void> = SmsRetriever.getClient(reactContext.currentActivity!!).startSmsUserConsent(null)
task.addOnSuccessListener { // successfully started an SMS Retriever for one SMS message
registerReceiver()
}
task.addOnFailureListener {
}
}
private fun registerReceiver() {
receiver = SmsRetrieveBroadcastReceiver(reactContext.currentActivity)
val intentFilter = IntentFilter(SmsRetriever.SMS_RETRIEVED_ACTION)
reactContext.currentActivity!!.registerReceiver(receiver, intentFilter)
}
@ReactMethod
private fun unregisterReceiver() {
try {
if (receiver != null) {
reactContext.currentActivity!!.unregisterReceiver(receiver)
receiver = null
}
} catch (e: Exception) {
e.message
}
}
private fun sendEvent(eventName: String,
@Nullable params: WritableMap) {
reactContext
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java) //supply the result in params
.emit(eventName, params)
}
companion object {
private const val STALE_DAYS = 5
private const val MY_REQUEST_CODE = 0
}
init {
reactContext.addActivityEventListener(mActivityEventListener)
}
}
4. SMSRetrivedPackage.kt
package com.otpdemo //Change your package name
import com.facebook.react.ReactPackage
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.bridge.NativeModule
import com.facebook.react.uimanager.ViewManager
import com.otpdemo.SMSRetrievedModules
import java.util.ArrayList
class SMSRetrivedPackage : ReactPackage {
override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> {
val modules: MutableList<NativeModule> = ArrayList()
modules.add(SMSRetrievedModules(reactContext))
return modules
}
override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
return emptyList()
}
}
Step – 3
Now change your MainApplication.kt file to import the SMSRetriver package.
override fun getPackages(): List<ReactPackage> {
// Packages that cannot be autolinked yet can be added manually here, for example:
val packages: MutableList<ReactPackage> = PackageList(this).packages
packages.add(SMSRetrivedPackage()) //Adding the SMSRetrivedPackage here manually
return packages
}
Step – 4
Now update your js or tsx file with this code.
import React, {useEffect, useState} from 'react';
import { Text, View, NativeModules, DeviceEventEmitter, Platform } from 'react-native';
const App = () => {
const SMSRetrived = NativeModules.SMSRetrived; //Getting module from NativeModules.
const [otp, setOTP] = useState('');
useEffect(() => {
setupMsgListener();
return () => {
DeviceEventEmitter.removeAllListeners('SMS_CONSTANT_EVENT');
};
}, []);
const setupMsgListener = async () => {
try {
if (Platform.OS == 'android') {
const getSMSMessage = await SMSRetrived.checkUpdate();
DeviceEventEmitter.addListener('SMS_CONSTANT_EVENT', listenOtp); //Adding Event liteners for SMS
}
} catch (e) {
// error
}
};
// Handler for recieved SMS
const listenOtp = (data: any) => {
if (data && data.receivedOtpMessage != null) {
var msg = data.receivedOtpMessage;
var code = (msg.match(/\d{6}/) || [false])[0];
setOTP(code); //Setting code in State
}
};
return (
<View style={{flex: 1, justifyContent: 'center', alignItems: 'center'}}>
<Text style={{fontSize: 18, color: '#000'}}>Received OTP: {otp}</Text>
</View>
);
};
export default App;
Conclusion:
While integrating automatic OTP reading in Android with React Native can pose challenges, especially when libraries fail, leveraging native Android code and proper bridging techniques offers a robust solution. At Creole Studios, our experienced React Native developers specialize in resolving such technical hurdles, ensuring seamless and secure user experiences in mobile applications. Our expertise in both React Native and native Android development helps bridge the gap effectively, delivering high-quality solutions tailored to your app’s needs.
FAQs:
- Why is automatic OTP reading not working in my React Native app?
Compatibility issues with Android versions or outdated libraries can cause OTP reading failures. Bridging native code can resolve these issues. - What is the SMS Retriever API, and how does it help with OTP reading?
The SMS Retriever API allows apps to automatically detect and retrieve OTPs from SMS messages without user intervention, enhancing user experience. - Do I need to write native Android code for OTP reading in React Native?
If existing libraries fail, writing native Android code and bridging it with React Native is a reliable solution for automatic OTP reading. - Is automatic OTP reading secure?
Yes, when implemented correctly with proper permissions and secure coding practices, automatic OTP reading enhances both security and user convenience.