Let's think about this for a moment
TextField is a view that captures text input from the user — you pair the `text:` parameter with a `$` binding to `@State` (from the previous lesson), since in SwiftUI a TextField doesn't hold internal state itself — it's controlled by the parent (the controlled-component pattern, similar to Compose/React). Button, meanwhile, is a view that triggers a trailing closure.
Let's connect this to a real-world scenario
Write `@State private var email = ""` and then `TextField("Email", text: $email)` — as the user types, the `email` state updates immediately, and the TextField in turn displays whatever text the user has typed on screen. Tapping a 'Submit' Button lets you validate/submit the `email` value.
Let's look at it together
struct LoginForm: View {
@State private var email = ""
@State private var password = ""
var body: some View {
VStack {
TextField("Email", text: $email)
.textFieldStyle(.roundedBorder)
SecureField("Password", text: $password)
.textFieldStyle(.roundedBorder)
Button("Login") {
print("Login: \(email)")
}
}
.padding()
}
}Type into both TextFields and tap the Login button — the Console should print 'Login: <email>'.Try it in 5 minutes
Run the `LoginForm` View — type in the email/password fields, tap the Login button, and check the Console output.
A quick word of caution
Don't rely on form validation (email format, password length) on the client side alone — in a production app you always need server-side validation too (client-side validation can be bypassed).