Introduction
UIAlertController supports adding text fields to alert dialogs for collecting user input. To get the text value, access the textFields array inside the action handler closure after the user taps a button. The text field is added with addTextField, configured in a closure, and read back via alertController.textFields?[0].text when the user confirms.
Basic Text Input Alert
1func showInputAlert() {
2 let alert = UIAlertController(
3 title: "Enter Name",
4 message: "Please enter your name below",
5 preferredStyle: .alert
6 )
7
8 // Add a text field
9 alert.addTextField { textField in
10 textField.placeholder = "Your name"
11 textField.autocapitalizationType = .words
12 }
13
14 // OK action — reads the text field value
15 let okAction = UIAlertAction(title: "OK", style: .default) { _ in
16 let name = alert.textFields?[0].text ?? ""
17 print("User entered: \(name)")
18 }
19
20 let cancelAction = UIAlertAction(title: "Cancel", style: .cancel)
21
22 alert.addAction(okAction)
23 alert.addAction(cancelAction)
24 present(alert, animated: true)
25}
The key pattern: alert.textFields?[0].text inside the action handler accesses the first text field's value after the user taps OK.
Multiple Text Fields
1func showLoginAlert() {
2 let alert = UIAlertController(
3 title: "Login",
4 message: "Enter your credentials",
5 preferredStyle: .alert
6 )
7
8 // Username field
9 alert.addTextField { textField in
10 textField.placeholder = "Username"
11 textField.autocorrectionType = .no
12 textField.autocapitalizationType = .none
13 }
14
15 // Password field
16 alert.addTextField { textField in
17 textField.placeholder = "Password"
18 textField.isSecureTextEntry = true
19 }
20
21 let loginAction = UIAlertAction(title: "Login", style: .default) { _ in
22 let username = alert.textFields?[0].text ?? ""
23 let password = alert.textFields?[1].text ?? ""
24 print("Username: \(username), Password: \(password)")
25 self.authenticate(username: username, password: password)
26 }
27
28 alert.addAction(loginAction)
29 alert.addAction(UIAlertAction(title: "Cancel", style: .cancel))
30 present(alert, animated: true)
31}
Text fields are indexed in the order they were added. The first addTextField call creates index 0, the second creates index 1.
Configuring Text Fields
1alert.addTextField { textField in
2 // Keyboard types
3 textField.keyboardType = .emailAddress // Email input
4 textField.keyboardType = .numberPad // Numbers only
5 textField.keyboardType = .phonePad // Phone number
6
7 // Text content
8 textField.text = "Default value" // Pre-filled text
9 textField.placeholder = "Enter email" // Placeholder
10 textField.clearButtonMode = .whileEditing // Clear button
11
12 // Behavior
13 textField.isSecureTextEntry = true // Password dots
14 textField.autocorrectionType = .no // Disable autocorrect
15 textField.autocapitalizationType = .none // No auto-caps
16 textField.returnKeyType = .done // Return key label
17
18 // Appearance
19 textField.textColor = .label
20 textField.font = .systemFont(ofSize: 16)
21}
1func showValidatedAlert() {
2 let alert = UIAlertController(
3 title: "Enter Email",
4 message: nil,
5 preferredStyle: .alert
6 )
7
8 alert.addTextField { textField in
9 textField.placeholder = "[email protected]"
10 textField.keyboardType = .emailAddress
11
12 // Observe text changes to enable/disable OK button
13 NotificationCenter.default.addObserver(
14 forName: UITextField.textDidChangeNotification,
15 object: textField,
16 queue: .main
17 ) { _ in
18 let text = textField.text ?? ""
19 alert.actions.first?.isEnabled = text.contains("@") && text.contains(".")
20 }
21 }
22
23 let okAction = UIAlertAction(title: "OK", style: .default) { _ in
24 let email = alert.textFields?[0].text ?? ""
25 print("Email: \(email)")
26 }
27 okAction.isEnabled = false // Start disabled
28
29 alert.addAction(okAction)
30 alert.addAction(UIAlertAction(title: "Cancel", style: .cancel))
31 present(alert, animated: true)
32}
Using a Completion Handler
For reusability, wrap the alert in a function with a completion handler:
1func promptForInput(
2 title: String,
3 message: String?,
4 placeholder: String?,
5 on viewController: UIViewController,
6 completion: @escaping (String?) -> Void
7) {
8 let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
9
10 alert.addTextField { textField in
11 textField.placeholder = placeholder
12 }
13
14 alert.addAction(UIAlertAction(title: "OK", style: .default) { _ in
15 completion(alert.textFields?.first?.text)
16 })
17
18 alert.addAction(UIAlertAction(title: "Cancel", style: .cancel) { _ in
19 completion(nil)
20 })
21
22 viewController.present(alert, animated: true)
23}
24
25// Usage
26promptForInput(title: "New Item", message: nil, placeholder: "Item name", on: self) { input in
27 guard let name = input, !name.isEmpty else { return }
28 self.addItem(name: name)
29}
async/await Version (iOS 13+)
1func promptForInput(title: String, placeholder: String?) async -> String? {
2 await withCheckedContinuation { continuation in
3 let alert = UIAlertController(title: title, message: nil, preferredStyle: .alert)
4
5 alert.addTextField { $0.placeholder = placeholder }
6
7 alert.addAction(UIAlertAction(title: "OK", style: .default) { _ in
8 continuation.resume(returning: alert.textFields?.first?.text)
9 })
10
11 alert.addAction(UIAlertAction(title: "Cancel", style: .cancel) { _ in
12 continuation.resume(returning: nil)
13 })
14
15 present(alert, animated: true)
16 }
17}
18
19// Usage
20Task {
21 if let name = await promptForInput(title: "Enter Name", placeholder: "Name") {
22 print("Got: \(name)")
23 }
24}
Common Pitfalls
Accessing textFields outside the action handler: The text fields exist when the alert is presented, but their values are only meaningful after the user taps an action button. Reading alert.textFields?[0].text immediately after present() returns the initial (empty or default) value, not what the user typed.
Using .actionSheet style with text fields: UIAlertController with .actionSheet style does not support text fields. Calling addTextField on an action sheet crashes at runtime. Always use .alert style for text input.
Forgetting optional chaining: alert.textFields is optional, and text on a UITextField is also optional. Using alert.textFields![0].text! crashes if the array is empty or text is nil. Always use alert.textFields?[0].text ?? "".
Strong reference to self in action closures: Action handler closures capture self strongly by default. For view controllers, this can delay deallocation. Use [weak self] in the closure if the alert handler triggers async work.
Not presenting on the main thread: UIAlertController must be presented from the main thread. If you call present() from a background queue (e.g., inside a network callback), the alert may not appear or may crash.
Summary
Add text fields with alert.addTextField { ... } and configure in the closure
Read values via alert.textFields?[0].text inside the action handler
Multiple text fields are indexed in the order added (0, 1, 2, ...)
Use NotificationCenter to observe text changes and disable the OK button until input is valid
Only .alert style supports text fields — .actionSheet does not
Wrap in a completion handler or async/await for reusable input prompts