SecureField dots invisible in dark mode when iOS suggests and fills a strong password

I am using SwiftUI's native SecureField. When a user types their password manually, the dots render correctly in both light and dark mode. However, when iOS suggests and autofills a strong generated password, the dots become invisible in dark mode. Switching to light mode shows that they are there.

Is there a supported way to force SecureField to re-render its secure entry dots correctly after iOS fills in a strong generated password in dark mode?

import SwiftUI

let warmMustard  = Color(red: 0.780, green: 0.659, blue: 0.290)
let lightText    = Color(red: 0.973, green: 0.961, blue: 0.933)
let darkText     = Color(red: 0.118, green: 0.118, blue: 0.118)

struct SecureFieldTestView: View {
    @Environment(\.colorScheme) var colorScheme
    @State private var username        = ""
    @State private var password        = ""
    @State private var confirmPassword = ""

    var body: some View {

        ZStack {
            Color(colorScheme == .dark ? UIColor.black : UIColor.white)
                .ignoresSafeArea()

            VStack(spacing: 20) {
                Text("Dark mode dot reproduction")
                    .foregroundColor(colorScheme == .dark ? .white : .black)

                TextField("Username", text: $username)
                    .textContentType(.username)
                    .autocorrectionDisabled()
                    .textInputAutocapitalization(.never)
                    .padding()
                    .background(colorScheme == .dark ? Color.black : Color.white)
                    .cornerRadius(8)
                    .foregroundColor(colorScheme == .dark ? .white : .black)
                    .overlay(RoundedRectangle(cornerRadius: 8).stroke(warmMustard, lineWidth: 2))

                SecureField("Password", text: $password)
                    .textContentType(.newPassword)
                    .padding()
                    .background(colorScheme == .dark ? Color.black : Color.white)
                    .cornerRadius(8)
                    .foregroundColor(colorScheme == .dark ? .white : .black)
                    .overlay(RoundedRectangle(cornerRadius: 8).stroke(warmMustard, lineWidth: 2))

                SecureField("Confirm Password", text: $confirmPassword)
                    .textContentType(.newPassword)
                    .padding()
                    .background(colorScheme == .dark ? Color.black : Color.white)
                    .cornerRadius(8)
                    .foregroundColor(colorScheme == .dark ? .white : .black)
                    .overlay(RoundedRectangle(cornerRadius: 8).stroke(warmMustard, lineWidth: 2))
            }
            .padding(.horizontal, 32)
        }
    }
}

#Preview {
    SecureFieldTestView()
}

@SergioDCQ Thanks for the interesting observation. I didn't copy your code but looking at the dark setting I got the idea of what is happening, I think. When iOS suggests and fills a strong password, it forces the underlying TextField's background to yellow and its text color to black (to ensure the text is readable against the yellow highlight).

When the yellow highlight fades out, the system is supposed to restore the original text color. However, because you are using a hardcoded color (colorScheme == .dark ? .white : .black) Since SwiftUI's state hasn't changed, it never re-applies the .white color, leaving you with invisible black dots on a black background.

Instead of manually switching between .white and .black, use SwiftUI's semantic .primary color (or Color(uiColor: .label)). Replace your .foregroundColor modifiers on the TextFields with .foregroundStyle(.primary) you can force SwiftUI to re-apply the text color by slightly changing the color value when the password is autofilled. By toggling between pure white and 99% opaque white, SwiftUI detects a state change and pushes the correct color back to the text field.

Would be very interesting to play with a simple focus project to see if I understand correctly.

Albert
  Worldwide Developer Relations.

@SergioDCQ Thanks for the interesting observation. I didn't copy your code but looking at the dark setting I got the idea of what is happening, I think. When iOS suggests and fills a strong password, it forces the underlying TextField's background to yellow and its text color to black (to ensure the text is readable against the yellow highlight).

When the yellow highlight fades out, the system is supposed to restore the original text color. However, because you are using a hardcoded color (colorScheme == .dark ? .white : .black) Since SwiftUI's state hasn't changed, it never re-applies the .white color, leaving you with invisible black dots on a black background.

Instead of manually switching between .white and .black, use SwiftUI's semantic .primary color (or Color(uiColor: .label)). Replace your .foregroundColor modifiers on the TextFields with .foregroundStyle(.primary) you can force SwiftUI to re-apply the text color by slightly changing the color value when the password is autofilled. By toggling between pure white and 99% opaque white, SwiftUI detects a state change and pushes the correct color back to the text field.

Would be very interesting to play with a simple focus project to see if I understand correctly. Albert
  Worldwide Developer Relations.

Thank you Albert. I tried foregroundStyle primary and systemBackground but the dots remain invisible on the confirm password field after autofill.

I discovered that textFieldDidChangeSelection only fires on whichever field has focus when the password is suggested. The confirm field gets filled silently with no delegate callback or SwiftUI state change, so I have no hook to trigger the color re-apply. How would you suggest forcing the color update on a field that receives autofill without focus?

SecureField dots invisible in dark mode when iOS suggests and fills a strong password
 
 
Q