How to use the @FocusState property wrapper in SwiftUI

Here’s how to make TextFields focused and not let a form close until the proper fields are filled out by the user.

WWDC 2021 introduced the property wrapper @FocusState. It can be used to set which textfields are in focus pretty easily. I’m going to open this form for adding a new bag of coffee an app I’m working on.

This article is actually a video and you can watch it now!

Notice how when I click on save it just closes the form and doesn’t dismiss the keyboard. The @FocusState can help fix that. First thing that needs to be added is an enum with the fields that we want to be required.

private enum Field {
    case name
    case style
    case roaster
}

The enum needs to conform to the Hashable protocol so you can either force conformance by adding : Hashable or as long as the cases don’t have a value assigned you should be good since Swift will do that “automagically”.

Now, we need to add a variable with the property wrapper @FocusState private var focusField: Field

Then on the TextFields, add the focused modifier and what field it should be equal to

TextField("Name", text: $beans.name)
    .textFieldStyle(.roundedBorder)
    .focused($focusedField, equals: .name)
TextField("Style", text: $beans.style)
    .textFieldStyle(.roundedBorder)
    .focused($focusedField, equals: .style)

TextField("Roaster", text: $beans.roaster)
    .textFieldStyle(.roundedBorder)
    .focused($focusedField, equals: .roaster)

Next, let’s put some logic in to make sure these fields are filled and set the next focusedField

Button {
    if beans.name.isEmpty {
        focusedField = .name
    } else if beans.style.isEmpty {
        focusedField = .style
    } else if beans.roaster.isEmpty {
        focusedField = .roaster
    } else {
        if isEdit {
            beans.updateBean(context: viewContext)
        } else {
            beans.addBeansToData(context: viewContext)
            resetBeans()
        }
      showForm = false
    }
}

Also, after a successful save, we should set the focusedfield to nil and dismiss the keyboard

Button {
    if beans.name.isEmpty {
        focusedField = .name
    } else if beans.style.isEmpty {
        focusedField = .style
    } else if beans.roaster.isEmpty {
        focusedField = .roaster
    } else {
        if isEdit {
            beans.updateBean(context: viewContext)
        } else {
            beans.addBeansToData(context: viewContext)
            resetBeans()
        }
      showForm = false
      focusedField = nil
    }
}

Now, let’s run through the form one more time to just show it all together.

Hopefully that showed you how to use the @FocusState property wrapper. If you have any questions, then please drop a comment below and while your down there hit the like and subscribe button for more SwiftUI and app development videos. I’ll see you in the next one!