Best Practices for OTP Input Forms in iOS

August 14, 2025
Written by
Alvin Lee
Contributor
Opinions expressed by Twilio contributors are their own

Best Practices for OTP Input Forms in iOS

One-time passwords (OTPs) have become a go-to solution for secure user verification, bridging the gap between security and user experience. Whether delivered through SMS or email, OTPs provide a reliable way to confirm user identity and enable frictionless authentication.

The appeal of OTP verification lies in its simplicity—users don't need to remember complex passwords or download additional apps. For iOS developers, creating an effective OTP experience requires attention to several key implementation details that can make or break the user experience.

In this guide, we'll explore how to build a seamless, secure OTP verification flow for iOS apps.

Need OTP for other platforms? Check out our companion web HTML version.

OTP workflows and use cases

The basic flow for OTP verification looks like this:

  1. The user enters their phone number or email and submits the form to the app.
  2. The app generates a token (for example: 123456) and sends the token to the user’s phone number or email.
  3. The user enters the token into the OTP verification form.
  4. The app verifies the token.

This simple workflow has become nearly ubiquitous. The use cases for OTP verification include:

  1. Contact information verification: Whether it is with email, SMS, voice, or WhatsApp, OTP can be used to authenticate the user’s identity via their contact information.
  2. Two-factor authentication: OTP can also be used as a second authentication factor in combination with another—such as username and password—proving that the person logging in is the person who owns the account.
  3. Account recovery: OTP verification is a common method to provide users a way to restore access in case of lost passwords or other account recovery situations.

OTP has several advantages over other authentication methods. For example:

  • OTP verification is passwordless and does not require the user to remember yet another password.
  • OTP verification does not require a separate app, reducing friction.
  • When implemented well, OTP verification has a streamlined UX, as the OTP token will be auto-filled and submitted for the user. They do not need to exit the current app.

However, even with these advantages, keep in mind that no mechanism is perfectly secure. The security of OTP verification depends on the security of the underlying phone number or email account. Additionally, phishing and recycled phone numbers can present issues.

Let’s look at how we can streamline the process and secure it.

Best Practices for OTP Verification on iOS

To streamline and secure the OTP Verification process, follow these best practices:

  1. Decide on the design of the input field
  2. Create a proper input field.
  3. Send the user a domain-bound OTP to enable autofill.

Decide on the design of the input field

For the input field of the OTP, you can either use a single input field or multiple input fields (one for each digit). If you’re having trouble choosing, the most common approach is opting for the multiple input field solution. This selection makes it clear to users how many digits they must input, and it uses all of the available screen space. It also provides a way to highlight to the user which digit they are currently entering.

However, multiple input fields add complexity regarding handling cursor movement between fields. Additionally, you must also handle autofill or pasting the OTP code into the fields. These problems are solvable, but do require some extra effort. We cover some solutions to these problems later in the article with a few troubleshooting tips.

Using a single input field is generally simpler, as it simplifies the implementation of autofilling and/or pasting. A single input field does not need any additional logic to handle cursor management.

A hybrid solution is also possible, where a single input field is styled as multiple inputs. Visually, the user will see multiple inputs, but on the backend, your app treats this as a single input. With an awareness of the tradeoffs involved, any of the above approaches can yield an effective OTP input field that provides a smooth user experience.

We'll show examples of how to build this with SwiftUI.

Create the OTP input field using a single input field

Here is an example of a single input OTP field using SwiftUI.

TextField("123456", text: $code)
    .font(.system(size: 28, weight: .medium, design: .monospaced))
    .multilineTextAlignment(.center)
    .keyboardType(.numberPad)
    .textContentType(.oneTimeCode)

Note the following key takeaways from this example:

  • keyboardType is set to numberPad. This tells the system to use the numeric keypad for entering the OTP.
  • textContentType is set to oneTimeCode. This tells the system to use AutoFill for the field when a domain-bound SMS code is received. For more on this, see the Apple Developer documentation.

A single input field makes accessibility and keyboard management simple. The interaction is direct—you tap where you type. Native text selection and cursor behavior are maintained, along with built-in iOS text editing gestures.

Create the OTP input field for multiple digits

In the case of implementing a multiple digit design, you would use a hidden text field with a visual representation on top. Here is an example using ZStack in SwiftUI:

ZStack {
    // Hidden text field for actual input
    TextField("", text: $otpText)
        .keyboardType(.numberPad)
        .textContentType(.oneTimeCode)
        .opacity(0)
    
    // Visual representation
    HStack(spacing: 12) {
        ForEach(0..<maxDigits, id: \.self) { index in
            DigitBox(...)
        }
    } 
}

Here are some key things to keep in mind from the above example:

  • We create a ZStack containing a hidden text field as well as a visual representation.
  • On the hidden text field, we have set the keyboardType to numberPad and the textContentType to oneTimeCode as before. This ensures compatibility with the iOS numeric keypad and with AutoFill, as in the single input version.
  • The visual representation dynamically adjusts to the number of digits needed.
  • We maintain a single source of truth through the otpText variable, avoiding potential sync issues.

With this implementation method, we still get the AutoFill of the single input version, but build our own visual layer on top. This gives us the freedom to freely customize the appearance according to brand guidelines or other design requirements.

Handling form submission and autofill completion

Once users complete entering their OTP—whether through autofill or manual input—you need to handle form submission gracefully. The key is detecting when the code is complete and deciding whether to auto-submit or wait for user action.

For automatic submission when the OTP is complete:

TextField("", text: $otpText)
    .keyboardType(.numberPad)
    .textContentType(.oneTimeCode)
    .onChange(of: otpText) { newValue in
        // Limit input length
        if newValue.count > maxDigits {
            otpText = String(newValue.prefix(maxDigits))
            return
        }
        
        // Auto-submit when complete
        if newValue.count == maxDigits {
            submitOTP(newValue)
        }
    }

This approach works particularly well with autofill, creating a seamless experience where the code appears and submits instantly. However, consider adding a brief delay (200-300ms) before auto-submission to give users a moment to see the completed code—especially for manual entry, where users might want to double-check their input.

For cases where you prefer manual submission, provide a submit button that becomes enabled when the code is complete.

Send the user a domain-bound OTP

To enable AutoFill in iOS, we need to send a domain-bound OTP. By providing the OTP in a specific format along with the domain, we instruct mobile systems to autofill that OTP only to our authorized domain. This increases the friction required for entering an OTP, which helps to prevent it from being entered on a fake domain.

First, you must associate the correct domain with your application. Add the Associated Domains Entitlement to your app, then add your domain to the entitlement.

Next, you must send the SMS in the proper format. The last line of the SMS must contain the @ symbol, your domain name, the # symbol, and then the one-time code. Here is an example of a properly formatted SMS:

123456 is your Example code.
@example.com #123456

You can set up domain-bound OTPs via Twilio Verify through the use of a custom template.

When used with an appropriately configured input field, this enables AutoFill—increasing ease of use, lowering friction, and decreasing the likelihood of phishing attacks on your OTP input.

Testing and Tips

After configuring your input field and SMS delivery, the following guidance will help you test your implementation.

Troubleshooting cursor management or AutoFill issues

If you are using multiple input fields, ensure that the user knows which digit they're currently entering. Because there are multiple fields, built-in cursor management doesn't apply. So, it is essential to provide that information to the user separately, using visual indicators such as border highlighting, contrasted background colors, or even a blinking cursor or line in the active field.

If you're having trouble with AutoFill, make sure that you have met the requirements:

  1. Associated the domain with the app
  2. Correctly formatted the last line of the SMS
  3. For your input field, set the textContentType to oneTimeCode

Testing and rate limiting

Keep in mind when testing that you are receiving real SMS and will be subject to the protective rate limits of the Verify API. Twilio Verify provides workarounds for testing, including completing or canceling the verification by calling specific endpoints.

Handling errors

Don’t forget to handle error cases. Your form should allow the user to resend and retry the OTP, but with a timeout to prevent bad actors. You can also provide alternative channels, such as voice calls, after the user has attempted to authenticate using other methods first.

Conclusion

Building an effective OTP verification system for iOS creates a user experience that feels both secure and effortless. When you combine intuitive input field design with iOS's built-in AutoFill capabilities, you're not just authenticating users; you're building trust and reducing friction in your app's most critical moments.

The implementation details we've covered—from choosing the right input approach to formatting domain-bound SMS messages—work together to create a verification flow that users barely notice, which is precisely what you want. When OTP verification works seamlessly, users can focus on what they came to your app to do.

Additional Resources