Formatting a UITextField for credit card input like xxxx xxxx xxxx xxxx
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
Formatting a credit card field as xxxx xxxx xxxx xxxx is a common iOS UI task because grouped digits are easier to verify visually than one long number. The usual approach is to intercept each edit, strip non-digits, cap the length, and then reinsert spaces every four digits before updating the UITextField.
Format the text in the delegate
The simplest implementation lives in textField(_:shouldChangeCharactersIn:replacementString:). You let the user type normally, rebuild the string from digits only, and then replace the field text with the formatted version.
This gives you grouped output on every keystroke and also sanitizes pasted input. If a user pastes 4242424242424242, the field still becomes 4242 4242 4242 4242.
Why rebuilding from digits is better than inserting spaces manually
A lot of first attempts try to insert a space only when the length reaches 4, 8, or 12. That works until the user deletes, pastes, or edits the middle of the string. Rebuilding from digits each time is more robust because the formatter always works from a clean canonical value.
This model also separates storage from presentation:
- The canonical value is the digits-only string.
- The visible value is the grouped text shown to the user.
That separation is important because payment APIs generally expect the raw digits, not the formatted text with spaces.
Extract the raw card number when submitting
When the user taps the pay button, remove the spaces before sending the value anywhere:
This keeps the UI user-friendly without polluting the backend or validation logic with formatting characters.
Consider variable card lengths and schemes
The 4-4-4-4 grouping is common, but not universal. American Express often uses 4-6-5, and some cards are shorter or longer than 16 digits. If the field must support multiple brands, make the grouping dynamic based on a detected card type instead of hardcoding a single pattern.
A simple extension point looks like this:
The formatter can then apply a group array rather than assuming fixed groups of four. Even if you start with the simpler 4-4-4-4 case, it is worth keeping the code organized so brand-specific rules can be added later.
User experience details that matter
Formatting alone is not enough for a good payment field. Set the keyboard to .numberPad, show a clear placeholder, and consider using textContentType where appropriate. Many apps also pair formatting with lightweight validation such as a Luhn check after editing ends, rather than showing an error while the user is still typing.
If you need perfect cursor handling for editing in the middle of the field, you must also compute the new caret position after reformatting. The simplified example above works well for ordinary typing and pasting, but middle edits may move the caret to the end because you are replacing the full text value.
Common Pitfalls
The most common bug is formatting the visible string repeatedly without first stripping spaces. That eventually produces malformed output such as doubled separators or incorrect lengths.
Another issue is sending the formatted string to the backend. Payment services almost always want the raw digits, so remove spaces before validation and submission.
Developers also hardcode a 16-digit maximum without thinking about card types. That is acceptable for a narrow use case, but broader payment forms should account for different issuers.
Finally, be careful with cursor behavior. Replacing the entire field text is simple, but it can feel jumpy when users edit in the middle unless you also restore the selection range intelligently.
Summary
- Intercept text changes in the delegate, strip non-digits, and rebuild the grouped display value.
- Keep a digits-only canonical value and treat the spaces as presentation only.
- Reformatting from scratch on every edit is more reliable than manual space insertion rules.
- Extract the raw number before validation or submission.
- If the field must support multiple card brands, make grouping and max length configurable.

