1.5.5 • Published 5 months ago

react-native-otp-receiver v1.5.5

Weekly downloads
-
License
MIT
Repository
github
Last release
5 months ago

React Native OTP Receiver

Introduction

This package handles receiving and filling OTP from SMS message. While automatically filling the input from incoming SMS is only available for Android, we handle user pressing the autofill button on iOS keyboard.

!IMPORTANT
For everything to work correctly, you must send SMS in correct format and setup associated domains for iOS

AndroidiOS
AndroidiOS

Supported Versions

This libs is currently available only for the new arch.

Installation

yarn add @pavelgric/react-native-otp-receiver

Usage

We offer various ways of integration, what could be call low-level and high-level.

In low-level, you can use methods exported for Android to capture incoming SMS and handle iOS your own way

In high-level you use one of the prepared components (you are still responsible for rendering the UI) to easily access and render the OTP. This also has iOS integration ready.

low-level usage (Android only)

This works in two steps. You register a listener for incoming SMS and then call a method that you are expecting a SMS.

onSMSReceived

A listener for incoming SMS.

import { onSMSReceived } from 'react-native-otp-receiver';

useEffect(() => {
  const listener = onSMSReceived((sms: SMSData) => {
    // handle the oncoming sms
  });
  return () => {
    listener.remove();
  };
}, []);

the listener receives SMSData with:

{
  message?: string;
  error?: string;
}

The error could be because of timeout, see expectSMSWithOTP

expectSMSWithOTP

After calling this function, you will get notified about incoming message via the onSMSReceived listener. SMS must be correctly formatted, otherwise you won't receive it from the onSMSReceived.

There is a time window to receive the message (around 5 mins) after calling expectSMSWithOTP, after this time window passes, onSMSReceived will receive "error": "TIMEOUT", after that no message will be captured and you may want to start expectSMSWithOTP again.

After calling expectSMSWithOTP, the onSMSReceived will capture only one message. If it's not the message you expected, call expectSMSWithOTP again (but for this to happen, user would have to receive SMS containing the app hash at end, it doesn't matter if user receives any other SMS).

import { expectSMSWithOTP } from 'react-native-otp-receiver';

const res = await expectSMSWithOTP()
// the res is boolean indicating whether the listener was started successfully

requestPhoneHint

Calling this function will display prompt for user to select his phone number.

import { requestPhoneHint } from 'react-native-otp-receiver';

const res = await requestPhoneHint()
// the res is a string containing the phoneNumber. This call may fail (e.g. user dismisses the prompt)

high-level usage

We try to assume as little as possible about what your code looks like and you should be able to configure following components to your needs.

With OTPProvider

Props
PropRequiredTypeDescriptionDefault
codeInputShapeYesnumber[]Shape for the code input. Number values in the array say how many characters are in each input cell.E.g. 1,1,1,1 is 4 cells with single char in each cell. 3, 3 are 3 chars in two cells-
childrenNoReactNodeThe UI to render-
onCodeEnteredNo(data: OnCodeEnteredData) => voidFunction called after user fills in the code in it's full length-
onSubmitCodeNo(data: OnSubmitCodeData) => void;Function called after user fills in the code in it's full length. In this callback you should do your code validation-
onCodeChangesNo(code: string) => voidCalled whenever the code value changes-
parseSMSNo(sms: string) => string \| null \| undefined(Android only) Function called when user receives the SMS and expects you to return the code extracted from it-
expectSMSOnMountNobooleanWhether to automatically call expectSMSWithOTP. If not set to true, you must call expectSMSWithOTP manually-
valueNostringUse to set the code input value manually-
parseEnteredCodeCharNo(char: string) => stringModify the entered char that user enters via keyboard. E.g. to capitalize it-
parsePastedCodeNo(code: string, codeInputShape: number[]) => string[];When user pastes copied (or suggested) value, here you can parse the value into input cells. E.g. to parse ABC-XYZ into ['ABC', 'XYZ']Function that separates string into the input shape
validateCodeCharNoCodeCharValidatorEach time the code changes, you can validate it. This code runs for each entered character. E.g. to only accept numbers-
onSMSErrorNo(error: string) => voidCalled when onSMSReceived returns error-

Example:

import { View, Button } from 'react-native';
import { OTPInputCell, OTPProvider } from 'react-native-otp-receiver';
import { useOTPContext } from '../../src/components/OTPProvider';
import styles from './styles';

const getCodeFromSMS = (sms: string) => {
  // find 6 digit code
  return sms.match(/\d{6}/)?.[0] || '';
};

export default function OTPProviderExample() {
  return (
    <OTPProvider
      codeInputShape={[1,1,1,1]} // 4 cells, each holds single char
      onCodeEntered={(code) => { /*  */ }}
      onSubmitCode={(data) => { /*   */}}
      parseSMS={getCodeFromSMS}
    >
      <CodeInputRenderer />
    </OTPProvider>
  );
}

function CodeInputRenderer() {
  const { codeInputs, isCodeValid, submitCode } = useOTPContext();

  return (
    <>
      <View style={styles.otpContainer}>
        {codeInputs.map((index) => {
          return <CodeInputComponent key={index} index={index} />;
        })}
      </View>
      <Button title="Submit" disabled={!isCodeValid} onPress={submitCode} />
    </>
  );
}

function CodeInputComponent({ index }: { index: number }) {
  return <OTPInputCell index={index} />;
}

Full example in the example app

With OTPInputHandler

Wraps the OTPProvider, some may prefer this implementation over using the provider.

Props

Provider props and:

PropRequiredTypeDescription
childrenYes(props: RenderProps) => ReactNodeThe UI to render
CodeInputCellComponentYes(otpHandles: CodeInputCellComponentProps) => ReactElementThe UI component to render as an input cell
import { View, Button, TouchableOpacity } from 'react-native';
import {
  OTPInputHandler,
  OTPInputCell,
  type CodeInputCellComponentProps,
} from 'react-native-otp-receiver';
import styles from './styles';

const getCodeFromSMS = (sms: string) => {
  return sms.match(/\d{6}/)?.[0] || '';
};

export default function OTPInputHandlerExample() {
  return (
    <OTPInputHandler
      codeInputShape={[1,1,1,1]}
      CodeInputCellComponent={CodeInputComponent}
      onCodeEntered={(code) => { /*  */ }}
      onSubmitCode={(code) => { /*  */}}
      parseSMS={getCodeFromSMS}
    >
      {({ renderOTPInput, submitCode, isValid }) => (
        <>
          <View style={styles.otpContainer}>{renderOTPInput()}</View>
          <Button title="Submit" disabled={!isValid} onPress={submitCode} />
        </>
      )}
    </OTPInputHandler>
  );
}

function CodeInputComponent(inputHandles: CodeInputCellComponentProps) {
  return <OTPInputCell {...inputHandles} />;
}

Full example in the example app

OTPInputCell

The cell that handles input of the code value. It's a wrapper around TextInput

Props

TextInput props and:

PropRequiredTypeDescription
indexYesnumberIndex of the input cell

hooks

useOTPInputCell

Returns handles to control TextInput component to capture key presses, text changes and others. Only useful if you do not use OTPInputCell component

useOTPInputCellState

Returns input cell state (like value, isFocused) and useful handles e.g. to focus input.

Generating app hash

You can get the app hash by running this command yarn react-native-otp-receiver get-app-hash. It will ask you for package name, path to your keystore file and the keystore password. This means you will have two separate app hashes, one for debug and one for production key.

Message content

Android

SMS must include app-hash at the end of the message, otherwise you won't receive it from the onSMSReceived. Other than that, the message content may be anything. It is up to you to retrieve the code value from the message content, based on what you send.

Example:

Your verification code is 123456

GawQ12fcSiS

iOS

You must set up associated domains for your app and add it to the SMS content. Than the message content must by as they say in apple docs

You can present any information at the start of the SMS message, in any format. The last line of your message needs your DNS domain name, prefixed by an at sign (@), and the one-time code, prefixed by a hash symbol (#). Separate the two components using a space. For example:

After receiving this SMS, user will will see a suggestion above their keyboard to fill this code. If you use high-level usage, pressing this code will be captured and filled automatically.

Example:

Your Example code is 123456.


@example.com #123456

iOS shortcomings

iOS doesn't have API for neither reading received SMS messages, nor to get own phone number

Contributing

See the contributing guide to learn how to contribute to the repository and the development workflow.

License

MIT


Made with create-react-native-library

1.5.5

5 months ago

1.5.4

5 months ago

1.5.3

5 months ago

1.5.2

5 months ago

1.5.1

5 months ago

1.5.0

5 months ago

1.4.0

5 months ago

1.3.3

5 months ago

1.3.2

5 months ago

1.3.1

5 months ago

1.3.0

5 months ago

1.2.0

5 months ago

1.1.0

5 months ago