UITextField
iOS development
Swift programming
event handling
text field listener

How do I check when a UITextField changes?

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Introduction

The usual way to detect text changes in a UITextField is to listen for the .editingChanged control event. That is the best default for most UIKit screens, but notifications and delegate methods are better in a few specific situations.

The Best Default: .editingChanged

For most forms, search boxes, and live validation, add a target for .editingChanged.

swift
1import UIKit
2
3final class LoginViewController: UIViewController {
4    private let emailField = UITextField()
5    private let continueButton = UIButton(type: .system)
6
7    override func viewDidLoad() {
8        super.viewDidLoad()
9
10        emailField.borderStyle = .roundedRect
11        emailField.addTarget(self,
12                             action: #selector(textFieldDidChange(_:)),
13                             for: .editingChanged)
14
15        continueButton.setTitle("Continue", for: .normal)
16        continueButton.isEnabled = false
17    }
18
19    @objc private func textFieldDidChange(_ textField: UITextField) {
20        let text = textField.text ?? ""
21        continueButton.isEnabled = text.contains("@")
22    }
23}

This is simple, immediate, and easy to reason about.

When To Use The Delegate Method Instead

If you need to inspect or reject the proposed edit before it is applied, use the delegate method textField(_:shouldChangeCharactersIn:replacementString:).

swift
1import UIKit
2
3final class ZipCodeViewController: UIViewController, UITextFieldDelegate {
4    private let zipField = UITextField()
5
6    override func viewDidLoad() {
7        super.viewDidLoad()
8        zipField.delegate = self
9        zipField.keyboardType = .numberPad
10    }
11
12    func textField(_ textField: UITextField,
13                   shouldChangeCharactersIn range: NSRange,
14                   replacementString string: String) -> Bool {
15        let current = textField.text ?? ""
16        guard let swiftRange = Range(range, in: current) else {
17            return false
18        }
19
20        let next = current.replacingCharacters(in: swiftRange, with: string)
21        return next.allSatisfy(\.isNumber) && next.count <= 5
22    }
23}

This is the right tool when the question is not merely "did the text change" but "should this change be allowed."

Notifications Are Useful For Decoupled Observers

If multiple objects need to react to changes, UITextField.textDidChangeNotification can be useful.

swift
1import UIKit
2
3final class ProfileViewController: UIViewController {
4    @IBOutlet private weak var nameField: UITextField!
5    private var observer: NSObjectProtocol?
6
7    override func viewDidLoad() {
8        super.viewDidLoad()
9
10        observer = NotificationCenter.default.addObserver(
11            forName: UITextField.textDidChangeNotification,
12            object: nameField,
13            queue: .main
14        ) { notification in
15            guard let field = notification.object as? UITextField else {
16                return
17            }
18            print("Current text:", field.text ?? "")
19        }
20    }
21
22    deinit {
23        if let observer {
24            NotificationCenter.default.removeObserver(observer)
25        }
26    }
27}

This is more flexible than target-action, but it is usually not the first choice for a single field in one view controller.

Debounce Expensive Work

If every change triggers network requests or costly filtering, react to changes but delay the heavy work.

swift
1import UIKit
2
3final class SearchViewController: UIViewController {
4    private let searchField = UITextField()
5    private var pendingWork: DispatchWorkItem?
6
7    override func viewDidLoad() {
8        super.viewDidLoad()
9        searchField.addTarget(self,
10                              action: #selector(queryChanged(_:)),
11                              for: .editingChanged)
12    }
13
14    @objc private func queryChanged(_ textField: UITextField) {
15        pendingWork?.cancel()
16
17        let work = DispatchWorkItem { [weak self] in
18            self?.performSearch(textField.text ?? "")
19        }
20
21        pendingWork = work
22        DispatchQueue.main.asyncAfter(deadline: .now() + 0.3, execute: work)
23    }
24
25    private func performSearch(_ query: String) {
26        print("Searching:", query)
27    }
28}

This keeps the UI responsive and avoids flooding the backend.

UIKit And SwiftUI Are Different

In UIKit, target-action and delegates are the normal patterns. In SwiftUI, a TextField is usually observed through @State, bindings, or view modifiers such as .onChange.

If you are writing UIKit code, stay in UIKit patterns instead of trying to force SwiftUI-style observation concepts into it.

Common Pitfalls

The most common mistake is using the delegate method to detect changes but forgetting that it gives you the proposed edit, not the final stored text unless you compute the next value yourself.

Another mistake is triggering expensive work on every keystroke without debounce or cancellation.

Developers also sometimes register notification observers and forget to clean them up, leading to duplicate callbacks or stale references.

Finally, do not scatter validation logic across target-action, notifications, and delegate methods unless there is a clear reason. Centralized input rules are easier to maintain.

Summary

  • Use .editingChanged for the usual UITextField change listener.
  • Use the delegate method when you need to approve or reject edits before they apply.
  • Use notifications when multiple observers need the event.
  • Debounce heavy reactions such as live search.
  • Keep the validation and update logic centralized so the field behavior stays predictable.

Related reading
Free course
Beginner
7 lessons
2 hours
Tackling System Design Interview Problems

A short course that equips you with the skills to approach system design interviews methodically.

Start the free course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

All Rights Reserved.