Password Strength Indicators

Visual password strength indicators help users create secure passwords. Here's how to build one.
Follow along with the code: iOS-Practice on GitHub
Strength Calculation
enum PasswordStrength {
case none, weak, medium, strong
var color: Color {
switch self {
case .none: return .gray
case .weak: return .red
case .medium: return .orange
case .strong: return .green
}
}
var label: String {
switch self {
case .none: return ""
case .weak: return "Weak"
case .medium: return "Medium"
case .strong: return "Strong"
}
}
}
Strength Algorithm
var passwordStrength: PasswordStrength {
if password.isEmpty { return .none }
if password.count < 8 { return .weak }
var score = 0
if password.count >= 12 { score += 1 }
if password.contains(where: { $0.isUppercase }) { score += 1 }
if password.contains(where: { $0.isNumber }) { score += 1 }
if password.contains(where: { "!@#$%^&*()".contains($0) }) { score += 1 }
switch score {
case 0...1: return .weak
case 2: return .medium
default: return .strong
}
}
Visual Indicator
struct PasswordStrengthView: View {
let strength: PasswordStrength
var body: some View {
HStack(spacing: 4) {
ForEach(0..<3) { index in
RoundedRectangle(cornerRadius: 2)
.fill(index < strengthLevel ? strength.color : Color.gray.opacity(0.3))
.frame(height: 4)
}
Text(strength.label)
.font(.caption)
.foregroundColor(strength.color)
}
}
var strengthLevel: Int {
switch strength {
case .none: return 0
case .weak: return 1
case .medium: return 2
case .strong: return 3
}
}
}
Integration
Section("Password") {
SecureField("Password", text: $formModel.password)
.textContentType(.newPassword)
if let error = formModel.passwordError {
Text(error)
.font(.caption)
.foregroundColor(.red)
}
PasswordStrengthView(strength: formModel.passwordStrength)
SecureField("Confirm Password", text: $formModel.confirmPassword)
if let error = formModel.confirmPasswordError {
Text(error)
.font(.caption)
.foregroundColor(.red)
}
}
Real-Time Updates
Because passwordStrength is a computed property on an ObservableObject, the indicator updates as the user types.
Customizing Criteria
Adjust the algorithm for your requirements:
var score = 0
// Length bonus
if password.count >= 12 { score += 1 }
if password.count >= 16 { score += 1 }
// Character variety
if password.contains(where: { $0.isUppercase }) { score += 1 }
if password.contains(where: { $0.isLowercase }) { score += 1 }
if password.contains(where: { $0.isNumber }) { score += 1 }
if password.contains(where: { "!@#$%^&*()_+-=[]{}|;:',.<>?".contains($0) }) { score += 1 }
// No common patterns
if !containsCommonPatterns(password) { score += 1 }
Alternative Visual Styles
Progress bar:
ProgressView(value: Double(strengthLevel) / 3.0)
.tint(strength.color)
Checkmarks:
HStack {
CheckItem("8+ characters", met: password.count >= 8)
CheckItem("Uppercase", met: password.contains(where: \.isUppercase))
CheckItem("Number", met: password.contains(where: \.isNumber))
}
Interview Tip
This demonstrates computed properties, enums with associated data, and reactive UI—all common SwiftUI patterns.