Skip to content
Prepare to Grow

SwiftUI Fundamentals

Controls, Forms, and User Input

Build accessible forms with native controls, validation, focus management, keyboard configuration, and clear submission behavior.

Intermediate6 min read2 guided activities

Before you begin

What you will understand

  • Choose semantic SwiftUI controls.
  • Connect controls to state and bindings.
  • Validate input without losing user data.
  • Manage focus, keyboard behavior, and submission accessibly.
01

Learning stage

Concept Explanation

SwiftUI controls communicate both appearance and behavior to the system. Button, Toggle, Picker, TextField, SecureField, Stepper, Slider, DatePicker, and Form adapt to platform conventions, accessibility technologies, tint, keyboard input, and interaction context. Prefer a semantic control over a visual gesture attached to an arbitrary shape.

Forms should preserve user input, explain requirements, and distinguish editing from submission. Validate fields as users can act on the feedback, but avoid showing every error before interaction begins. Disable submission only when the reason is visible or obvious, and revalidate at the domain boundary.

FocusState coordinates keyboard focus without making it domain data. Configure text content type, capitalization, keyboard type, submit label, and onSubmit so the field matches its meaning. Keyboard configuration improves input but never replaces validation.

  • Semantic control: conveys intent and system interaction behavior.
  • Binding: connects control value to a source of truth.
  • Validation: converts raw input into accepted domain data or a useful error.
  • @FocusState: represents temporary focus location.
  • Submit action: one explicit boundary for committing a valid draft.
02

Learning stage

Syntax & Examples

The form keeps raw input, derives validation, guides focus, and submits one validated value.

AccountForm.swiftswift
import SwiftUI

struct AccountForm: View {
  @State private var email = ""
  @State private var notificationsEnabled = true
  @FocusState private var focusedField: Field?

  enum Field { case email }

  private var trimmedEmail: String {
    email.trimmingCharacters(in: .whitespacesAndNewlines)
  }

  private var emailError: String? {
    guard !trimmedEmail.isEmpty else { return "Email is required." }
    return trimmedEmail.contains("@") ? nil : "Enter a valid email address."
  }

  var body: some View {
    Form {
      Section("Account") {
        TextField("Email", text: $email)
          .textContentType(.emailAddress)
          .textInputAutocapitalization(.never)
          .keyboardType(.emailAddress)
          .focused($focusedField, equals: .email)
          .submitLabel(.done)
          .onSubmit(save)

        if !email.isEmpty, let emailError {
          Text(emailError).foregroundStyle(.red)
        }
      }

      Toggle("Learning reminders", isOn: $notificationsEnabled)
      Button("Save", action: save).disabled(emailError != nil)
    }
  }

  private func save() {
    guard emailError == nil else { focusedField = .email; return }
    focusedField = nil
    // Pass the validated value to the feature model.
  }
}
Validation is derived from the draft. Focus is local UI state, while the final save boundary decides whether the value can enter the feature model.
03

Learning stage

Real-World Usage

Connect the SwiftUI concept to complete, maintainable application features.

  • Create settings, profile editors, onboarding, search, and checkout forms.
  • Use pickers and toggles that automatically expose accessible values.
  • Guide multi-field entry with focus and submit labels.
  • Preserve a draft when a modal is interrupted or validation fails.
04

Learning stage

Common Mistakes

Identify patterns that weaken ownership, identity, adaptation, accessibility, or user experience.

  • Using onTapGesture on styled text instead of Button.
  • Clearing user input after a validation failure.
  • Using keyboardType as if it guaranteed valid data.
  • Disabling Save without explaining what is incomplete.
  • Putting a separate independent source of truth behind every control and model property.
05

Learning stage

Practice Exercises

Build the behavior in a small Xcode project and review it in multiple states.

Activity 1

Build a validated profile form

Create name, email, experience-level, reminder, and start-date fields. Show useful validation and move focus between text fields.

Hint: Keep raw draft input until Save succeeds.

Activity 2

Replace custom interactions

Find three tappable HStacks or Images in a sample screen and replace them with Button, Toggle, or NavigationLink as appropriate.

Hint: Preserve the desired appearance with styles while keeping semantic behavior.

06

Learning stage

Knowledge Check

Answer each question before revealing the explanation.

QUESTION 1

Why prefer Button to onTapGesture for a normal action?

  1. A.Button communicates action semantics and supports system interaction behavior
  2. B.Button always uses a blue background
  3. C.Gestures cannot detect taps
  4. D.Button stores data automatically
Reveal answer

Button communicates action semantics and supports system interaction behavior

It integrates with accessibility, keyboard activation, roles, styles, and platform conventions.

QUESTION 2

Does keyboardType validate a field's value?

Reveal answer

No. It only influences the input interface; the value still requires validation.

Users can paste text and hardware keyboards are not restricted by the software keyboard layout.

07

Learning stage

Interview Preparation

Explain the design in terms of data flow, identity, lifecycle, platform behavior, and trade-offs.

How would you design reliable form state in SwiftUI?

Short answer: Keep a draft source of truth, derive validation, use semantic controls, and commit validated values at one explicit boundary.

Explore the detailed answer

The form owns temporary editing state and focus. Controls bind to that draft. Validation messages appear when useful, submission revalidates, and only accepted domain values are passed to the model. This protects the user's input and prevents partially valid data from leaking into application state.

Possible follow-ups

  • • When should validation appear?
  • • What belongs in FocusState?
08

Learning stage

Key Takeaways

Good SwiftUI forms use semantic controls, preserve draft input, and create one clear validation and submission boundary. Focus and keyboard configuration improve the interaction but do not replace data rules.

  • Use native semantic controls.
  • Bind controls to a clear draft source of truth.
  • Derive and communicate validation.
  • Treat focus as temporary UI state.
  • Revalidate before committing domain data.