How To Create Otp TextField In SwiftUI
Tags: swift, mobile, beginners, programming
Published: 2023-05-29 11:07:35

# Introduction

Having just started building native IOS applications using SwiftUI, It is different from building mobile apps using React Native. I struggled in making some components since I have less experience building Native Applications.

One of them is OTP InputField.

So in this article, I will be teaching you how I create my OTP Form.

### Mindset

Building an OTP InputField is very simple if you understand the rules to follow.

* Each field will not accept more than one value
    
* On value, the input should change focus to the next one
    

Let's get started; by now I assume you have created a new project in your Xcode.

First, I will love to create a modifier for my InputFields.

Create a new SwiftUi View file **OtpModifier.swift**

```swift
import SwiftUI
import Combine

struct OtpModifer: ViewModifier {
    
    @Binding var pin : String
    
    var textLimt = 1

    func limitText(_ upper : Int) {
        if pin.count > upper {
            self.pin = String(pin.prefix(upper))
        }
    }
    
    
    //MARK -> BODY
    func body(content: Content) -> some View {
        content
            .multilineTextAlignment(.center)
            .keyboardType(.numberPad)
            .onReceive(Just(pin)) {_ in limitText(textLimt)}
            .frame(width: 45, height: 45)
            .background(Color.white.cornerRadius(5))
            .background(
                RoundedRectangle(cornerRadius: 5)
                    .stroke(Color("blueColor"), lineWidth: 2)
            )
    }
}
```

with this code the binding variable `pin` will be holding the value from our input field, and the variable `textlimit` will be a placeholder for how many characters or the length of the value we want our input to receive, in my case I want each input to hold just one value, so my `textlimit` variable is one.

```swift
 func limitText(_ upper : Int) {
        if pin.count > upper {
            self.pin = String(pin.prefix(upper))
        }
    }
```

The function `limitText` above takes an argument of type Integer <mark>upper</mark> and checks if the value on the input is greater than the upper argument. If true, it will assign `pin` a new value that returns just the first character in the initial value provided.

Create another SwiftUi View file **OtpFormFieldView.swift**

```swift
import SwiftUI
import Combine

struct OtpFormFieldView: View {
    //MARK -> PROPERTIES
    
    enum FocusPin {
        case  pinOne, pinTwo, pinThree, pinFour
    }
    
    @FocusState private var pinFocusState : FocusPin?
    @State var pinOne: String = ""
    @State var pinTwo: String = ""
    @State var pinThree: String = ""
    @State var pinFour: String = ""
    
    
    //MARK -> BODY
    var body: some View {
            VStack {
    
                Text("Verify your Email Address")
                    .font(.title2)
                    .fontWeight(.semibold)
                
                   
                Text("Enter 4 digit code we'll text you on Email")
                    .font(.caption)
                    .fontWeight(.thin)
                    .padding(.top)
               
                HStack(spacing:15, content: {
                    
                    TextField("", text: $pinOne)
                        .modifier(OtpModifer(pin:$pinOne))
                        .onChange(of:pinOne){newVal in
                            if (newVal.count == 1) {
                                pinFocusState = .pinTwo
                            }
                        }
                        .focused($pinFocusState, equals: .pinOne)
                    
                    TextField("", text:  $pinTwo)
                        .modifier(OtpModifer(pin:$pinTwo))
                        .onChange(of:pinTwo){newVal in
                            if (newVal.count == 1) {
                                pinFocusState = .pinThree
                            }
                        }
                        .focused($pinFocusState, equals: .pinTwo)

                    
                    TextField("", text:$pinThree)
                        .modifier(OtpModifer(pin:$pinThree))
                        .onChange(of:pinThree){newVal in
                            if (newVal.count == 1) {
                                pinFocusState = .pinFour
                            }
                        }
                        .focused($pinFocusState, equals: .pinThree)

                    
                    TextField("", text:$pinFour)
                        .modifier(OtpModifer(pin:$pinFour))
                        .focused($pinFocusState, equals: .pinFour)

                        
                })
                .padding(.vertical)
            
                
                Button(action: {}, label: {
                    Spacer()
                    Text("Veify")
                        .font(.system(.title3, design: .rounded))
                        .fontWeight(.semibold)
                        .foregroundColor(.white)
                    Spacer()
                })
                .padding(15)
                .background(Color.blue)
                .clipShape(Capsule())
                .padding()
            }
        
    }
}

struct OtpFormFieldView_Previews: PreviewProvider {
    static var previews: some View {
        OtpFormFieldView()
    }
}
```

Now in this file, we combined what we have been building with a couple of new things. These new things include `FocusState` FocusState is helping us keep an eye on the inputField the user is currently typing on and notifies us when the input is up to the `textlimit`

We have specified in our case when the input receives a value that is equal to **one,** it will notify our FocusState and then the focus will be changed to the next input till we get to the last inputField

```swift
import SwiftUI
import Combine

struct OtpFormFieldView: View {
    //MARK -> PROPERTIES
    
    enum FocusPin {
        case  pinOne, pinTwo, pinThree, pinFour
    }
    
    @FocusState private var pinFocusState : FocusPin?
    @State var pinOne: String = ""
    @State var pinTwo: String = ""
    @State var pinThree: String = ""
    @State var pinFour: String = ""
    
    
    //MARK -> BODY
    var body: some View {
            VStack {
    
                Text("Verify your Email Address")
                    .font(.title2)
                    .fontWeight(.semibold)
                
                   
                Text("Enter 4 digit code we'll text you on Email")
                    .font(.caption)
                    .fontWeight(.thin)
                    .padding(.top)
               
                HStack(spacing:15, content: {
                    
                    TextField("", text: $pinOne)
                        .modifier(OtpModifer(pin:$pinOne))
                        .onChange(of:pinOne){newVal in
                            if (newVal.count == 1) {
                                pinFocusState = .pinTwo
                            }
                        }
                        .focused($pinFocusState, equals: .pinOne)
                    
                    TextField("", text:  $pinTwo)
                        .modifier(OtpModifer(pin:$pinTwo))
                        .onChange(of:pinTwo){newVal in
                            if (newVal.count == 1) {
                                pinFocusState = .pinThree
                            }else {
                                if (newVal.count == 0) {
                                    pinFocusState = .pinOne
                                }
                            }
                        }
                        .focused($pinFocusState, equals: .pinTwo)

                    
                    TextField("", text:$pinThree)
                        .modifier(OtpModifer(pin:$pinThree))
                        .onChange(of:pinThree){newVal in
                            if (newVal.count == 1) {
                                pinFocusState = .pinFour
                            }else {
                                if (newVal.count == 0) {
                                    pinFocusState = .pinTwo
                                }
                            }
                        }
                        .focused($pinFocusState, equals: .pinThree)

                    
                    TextField("", text:$pinFour)
                        .modifier(OtpModifer(pin:$pinFour))
                        .onChange(of:pinFour){newVal in
                            if (newVal.count == 0) {
                                pinFocusState = .pinThree
                            }
                        }
                        .focused($pinFocusState, equals: .pinFour)

                        
                })
                .padding(.vertical)
            
                
                Button(action: {}, label: {
                    Spacer()
                    Text("Veify")
                        .font(.system(.title3, design: .rounded))
                        .fontWeight(.semibold)
                        .foregroundColor(.white)
                    Spacer()
                })
                .padding(15)
                .background(Color.blue)
                .clipShape(Capsule())
                .padding()
            }
        
    }
}

struct OtpFormFieldView_Previews: PreviewProvider {
    static var previews: some View {
        OtpFormFieldView()
    }
}
```

I updated the OtpFormFieldView.swift, allowing It to change focus to the previous InputField if the user deletes the input value. Now we have a functional OTP InputField to build on.

### Preview

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/avn9ic16v3h4sfklia89.gif)

## Link

You can get the code from this article from [GitHub](https://github.com/Mremmalex/swiftui_otp_Inputfield)

# Conclusion

I know this will not be the most efficient way of achieving this, but this works for my use case, and you can update the code based on how many inputField you want for your use case