Swift
UITextField
iOS Development
Number Input
Programming Tips

How to restrict UITextField to take only numbers in Swift?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Restricting a UITextField to numeric input requires more than setting a numeric keyboard. Users can still paste text, use hardware keyboards, or enter locale-specific separators. A robust implementation combines keyboard hints, delegate validation, and final parsing rules.

Configure Keyboard for Better UX

Start with a keyboard type that matches expected input.

swift
1import UIKit
2
3final class AmountViewController: UIViewController {
4    @IBOutlet private weak var amountField: UITextField!
5
6    override func viewDidLoad() {
7        super.viewDidLoad()
8        amountField.keyboardType = .numberPad
9        amountField.placeholder = "Enter amount"
10    }
11}

This improves user experience but does not guarantee valid input.

Enforce Input in Delegate Method

Use textField should change characters in range replacement string to validate candidate text.

swift
1import UIKit
2
3final class NumericFieldDelegate: NSObject, UITextFieldDelegate {
4    let maxLength: Int
5
6    init(maxLength: Int = 9) {
7        self.maxLength = maxLength
8    }
9
10    func textField(
11        _ textField: UITextField,
12        shouldChangeCharactersIn range: NSRange,
13        replacementString string: String
14    ) -> Bool {
15        let current = textField.text ?? ""
16        guard let textRange = Range(range, in: current) else { return false }
17
18        let candidate = current.replacingCharacters(in: textRange, with: string)
19        if candidate.count > maxLength { return false }
20
21        let digits = CharacterSet.decimalDigits
22        return candidate.unicodeScalars.allSatisfy(digits.contains)
23    }
24}

Attach this delegate to your text field in viewDidLoad.

Support Decimal Input Safely

If decimals are allowed, handle locale-aware separators.

swift
1func isValidDecimalCandidate(_ text: String, locale: Locale = .current) -> Bool {
2    let separator = locale.decimalSeparator ?? "."
3    let allowed = CharacterSet(charactersIn: "0123456789" + separator)
4
5    guard text.unicodeScalars.allSatisfy(allowed.contains) else { return false }
6    return text.components(separatedBy: separator).count <= 2
7}

Do not hardcode period as decimal separator for all users.

Validate Final Value on Submit

Editing validation helps typing flow, but final form submission should parse and validate business constraints.

swift
1func parseAmount(_ text: String) -> Int? {
2    guard !text.isEmpty else { return nil }
3    return Int(text)
4}

For decimal money values, parse with NumberFormatter and convert to a minor unit type if your backend expects cents.

Handle Paste and External Input Paths

Delegate validation covers typed and pasted input when applied correctly. Still, test these flows explicitly:

  • copy and paste long invalid strings
  • hardware keyboard entry in simulator
  • VoiceOver focus and editing behavior

Numeric restrictions that work only with on-screen keyboard are incomplete.

Reusable Component Pattern

If many screens need numeric fields, centralize logic with a subclass.

swift
1import UIKit
2
3final class NumericTextField: UITextField, UITextFieldDelegate {
4    override init(frame: CGRect) {
5        super.init(frame: frame)
6        setup()
7    }
8
9    required init?(coder: NSCoder) {
10        super.init(coder: coder)
11        setup()
12    }
13
14    private func setup() {
15        keyboardType = .numberPad
16        delegate = self
17    }
18
19    func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
20        let current = textField.text ?? ""
21        guard let textRange = Range(range, in: current) else { return false }
22        let candidate = current.replacingCharacters(in: textRange, with: string)
23        return candidate.unicodeScalars.allSatisfy(CharacterSet.decimalDigits.contains)
24    }
25}

This reduces repeated validation code and improves consistency.

Add Focused Input Tests

UI tests should cover typing, pasting, and deleting characters at different cursor positions. These scenarios catch edge cases where delegate logic looks correct but fails for replacement ranges in real user editing patterns.

Common Pitfalls

  • Relying only on keyboard type for validation.
  • Ignoring paste and hardware keyboard input.
  • Hardcoding decimal separator and breaking locale behavior.
  • Validating only on submit with poor user feedback.
  • Duplicating validation logic across many controllers.

Summary

  • Use numeric keyboard as guidance, not as full validation.
  • Enforce numeric rules in delegate callbacks.
  • Add locale-aware handling for decimal inputs.
  • Re-validate and parse values at submission time.
  • Centralize reusable input logic for consistency and maintainability.

Course illustration
Course illustration

All Rights Reserved.