iOS Development
Swift
UITextField
Password Security
User Interface

Obscure a UITextField password

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

Introduction

Setting isSecureTextEntry = true on a UITextField obscures the password by replacing characters with dots. This is the standard iOS approach and also disables text selection, auto-correction, and screenshot capture of the field content. For a password visibility toggle (show/hide button), combine isSecureTextEntry with a toggle button that switches the property at runtime. SwiftUI provides the built-in SecureField view for the same purpose.

Basic Password Field (UIKit)

swift
1import UIKit
2
3class LoginViewController: UIViewController {
4    let passwordField = UITextField()
5
6    override func viewDidLoad() {
7        super.viewDidLoad()
8
9        passwordField.isSecureTextEntry = true
10        passwordField.placeholder = "Enter password"
11        passwordField.borderStyle = .roundedRect
12        passwordField.textContentType = .password  // Enables Keychain autofill
13        passwordField.autocorrectionType = .no
14        passwordField.autocapitalizationType = .none
15
16        view.addSubview(passwordField)
17        // ... layout constraints
18    }
19}

Setting isSecureTextEntry = true does the following:

  • Replaces each character with a bullet dot after a brief moment
  • Disables text selection and copy/paste
  • Prevents the field content from appearing in screenshots
  • Disables predictive text and autocorrection

Interface Builder (Storyboard)

In Interface Builder, select the UITextField and in the Attributes Inspector:

  1. Check "Secure Text Entry" under the Text Input Traits section
  2. Set "Content Type" to "Password" for Keychain integration
  3. Set "Autocorrection" to "No"
  4. Set "Autocapitalization" to "None"

Password Visibility Toggle

swift
1import UIKit
2
3class PasswordField: UITextField {
4    private let toggleButton = UIButton(type: .custom)
5
6    override init(frame: CGRect) {
7        super.init(frame: frame)
8        setup()
9    }
10
11    required init?(coder: NSCoder) {
12        super.init(coder: coder)
13        setup()
14    }
15
16    private func setup() {
17        isSecureTextEntry = true
18        textContentType = .password
19        autocorrectionType = .no
20
21        // Configure toggle button
22        toggleButton.setImage(UIImage(systemName: "eye.slash"), for: .normal)
23        toggleButton.setImage(UIImage(systemName: "eye"), for: .selected)
24        toggleButton.tintColor = .gray
25        toggleButton.addTarget(self, action: #selector(toggleVisibility), for: .touchUpInside)
26        toggleButton.frame = CGRect(x: 0, y: 0, width: 40, height: 40)
27
28        rightView = toggleButton
29        rightViewMode = .always
30    }
31
32    @objc private func toggleVisibility() {
33        isSecureTextEntry.toggle()
34        toggleButton.isSelected = !isSecureTextEntry
35
36        // Fix cursor position after toggling
37        if let existingText = text, isFirstResponder {
38            deleteBackward()  // Clear and re-set to fix cursor
39            insertText(existingText)
40        }
41    }
42}
43
44// Usage
45let passwordField = PasswordField()
46passwordField.placeholder = "Password"

Handling the Cursor Position Bug

When toggling isSecureTextEntry while the field is focused, the cursor may jump to the beginning. The fix:

swift
1@objc func toggleSecureEntry() {
2    passwordField.isSecureTextEntry.toggle()
3
4    // Preserve cursor position
5    if let textRange = passwordField.textRange(
6        from: passwordField.endOfDocument,
7        to: passwordField.endOfDocument
8    ) {
9        passwordField.selectedTextRange = textRange
10    }
11}

SwiftUI SecureField

swift
1import SwiftUI
2
3struct LoginView: View {
4    @State private var password = ""
5    @State private var showPassword = false
6
7    var body: some View {
8        VStack(spacing: 16) {
9            // Simple secure field
10            SecureField("Password", text: $password)
11                .textFieldStyle(.roundedBorder)
12                .textContentType(.password)
13
14            // Toggle between SecureField and TextField
15            HStack {
16                if showPassword {
17                    TextField("Password", text: $password)
18                        .textFieldStyle(.roundedBorder)
19                        .textContentType(.password)
20                        .autocorrectionDisabled()
21                        .textInputAutocapitalization(.never)
22                } else {
23                    SecureField("Password", text: $password)
24                        .textFieldStyle(.roundedBorder)
25                        .textContentType(.password)
26                }
27
28                Button {
29                    showPassword.toggle()
30                } label: {
31                    Image(systemName: showPassword ? "eye" : "eye.slash")
32                        .foregroundColor(.gray)
33                }
34            }
35        }
36        .padding()
37    }
38}

Objective-C

objc
1// Basic secure field
2UITextField *passwordField = [[UITextField alloc] init];
3passwordField.secureTextEntry = YES;
4passwordField.placeholder = @"Enter password";
5passwordField.textContentType = UITextContentTypePassword;
6passwordField.autocorrectionType = UITextAutocorrectionTypeNo;
7
8// Toggle visibility
9- (void)togglePasswordVisibility:(UIButton *)sender {
10    self.passwordField.secureTextEntry = !self.passwordField.secureTextEntry;
11
12    // Fix cursor position
13    NSString *temp = self.passwordField.text;
14    self.passwordField.text = @"";
15    self.passwordField.text = temp;
16}

Password Strength Indicator

swift
1func passwordStrength(_ password: String) -> (String, UIColor) {
2    let length = password.count
3    let hasUpper = password.range(of: "[A-Z]", options: .regularExpression) != nil
4    let hasLower = password.range(of: "[a-z]", options: .regularExpression) != nil
5    let hasDigit = password.range(of: "[0-9]", options: .regularExpression) != nil
6    let hasSpecial = password.range(of: "[^A-Za-z0-9]", options: .regularExpression) != nil
7
8    let score = [hasUpper, hasLower, hasDigit, hasSpecial].filter { $0 }.count
9
10    if length < 8 { return ("Weak", .systemRed) }
11    if score < 3 { return ("Medium", .systemOrange) }
12    return ("Strong", .systemGreen)
13}

Common Pitfalls

  • Toggling isSecureTextEntry resets the cursor: When switching between secure and plain text while the field is focused, iOS may reset the cursor to the beginning or clear the text. Save and restore the text and cursor position after toggling to avoid user frustration.
  • Not setting textContentType to .password: Without textContentType = .password, iOS does not offer Keychain autofill for the field. Users expect the password autofill prompt, especially with Face ID/Touch ID integration. Set it to .password for login and .newPassword for registration.
  • Allowing autocorrection on password fields: Autocorrection caches typed text in the keyboard dictionary, potentially leaking password fragments. Always set autocorrectionType = .no and autocapitalizationType = .none on password fields.
  • Using custom font with secure entry: Setting a custom font on a secure text field may cause the bullet dots to render at the wrong size or position on some iOS versions. Test with your custom font, or use the system font for the secure state.
  • Screenshots still capturing the field in SwiftUI: SecureField in SwiftUI hides content from screenshots, but switching to a TextField for the "show password" state makes it capturable again. If screenshot protection is required, keep the field as SecureField and disable the show toggle.

Summary

  • Set isSecureTextEntry = true on UITextField to obscure password characters with dots
  • Use SecureField in SwiftUI for the same functionality with less code
  • Add a toggle button to switch between isSecureTextEntry = true/false for show/hide password
  • Set textContentType = .password to enable Keychain autofill and Face ID/Touch ID
  • Disable autocorrection and autocapitalization on all password fields
  • Fix the cursor position bug when toggling secure entry by resetting the text range

Related reading
Course
Beginner
27 lessons
10 hours
System Design Fundamentals

Build a strong foundation in designing scalable, reliable distributed systems.

View the course
Track what you have practised

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

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

All Rights Reserved.