fixed credit card issues with formatting for different card types also added an min/max length based on card type.

Signed-off-by: Matt Bruce <matt.bruce@verizon.com>
This commit is contained in:
Matt Bruce 2024-06-06 12:22:21 -05:00
parent f5380532ff
commit 2e46759857

View File

@ -9,7 +9,20 @@ import Foundation
import UIKit import UIKit
extension InputField { extension InputField {
class CreditCardNumberRule: Rule, Withable {
var cardType: CreditCardType?
var errorMessage: String = "You have exceeded the character limit."
func isValid(value: String?) -> Bool {
guard let count = value?.count, let min = cardType?.minLength, let max = cardType?.maxLength else { return true }
if min == max {
return count == max
} else {
return count >= min && count <= max
}
}
}
public enum CreditCardType: String, CaseIterable { public enum CreditCardType: String, CaseIterable {
case generic case generic
case visa case visa
@ -38,15 +51,22 @@ extension InputField {
} }
} }
var separatorIndices: [Int] { func separatorIndices(_ length: Int) -> [Int] {
var indices: [Int] = [4, 8, 12]
switch self { switch self {
case .dinersClub: case .amex, .dinersClub:
return [4, 10] indices = [4, 10]
case .unionPay:
if length == 19 {
indices = [5]
}
default: default:
return [4, 8, 12] break
} }
return indices
} }
var securityCodeLength: Int { var securityCodeLength: Int {
if self == .amex { if self == .amex {
return 4 return 4
@ -55,9 +75,21 @@ extension InputField {
} }
} }
var minLength: Int {
switch self {
case .visa: return 13
case .amex: return 15
case .dinersClub: return 14
default: return 16
}
}
var maxLength: Int { var maxLength: Int {
switch self { switch self {
case .visa: return 19
case .amex: return 15
case .dinersClub: return 14 case .dinersClub: return 14
case .unionPay: return 19
default: return 16 default: return 16
} }
} }
@ -131,9 +163,8 @@ extension InputField {
override func appendRules(_ inputField: InputField) { override func appendRules(_ inputField: InputField) {
if let text = inputField.textField.text, text.count > 0 { if let text = inputField.textField.text, text.count > 0 {
let rule = CharacterCountRule().copyWith { let rule = CreditCardNumberRule().copyWith {
$0.maxLength = inputField.cardType.maxLength $0.cardType = inputField.cardType
$0.compareType = .equals
$0.errorMessage = "Enter a valid credit card." $0.errorMessage = "Enter a valid credit card."
} }
inputField.rules.append(.init(rule)) inputField.rules.append(.init(rule))
@ -205,8 +236,8 @@ extension InputField {
/// Private /// Private
internal func formatCreditCardNumber(_ cardType: CreditCardType, number: String) -> String { internal func formatCreditCardNumber(_ cardType: CreditCardType, number: String) -> String {
let formattedInput = number.filter { $0.isNumber } // Remove any existing slashes let rawNumber = number.filter { $0.isNumber } // Remove any existing slashes
return String.format(formattedInput, indices: cardType.separatorIndices, with: " ") return String.format(rawNumber, indices: cardType.separatorIndices(rawNumber.count), with: " ")
} }
internal func updateCardTypeIcon(_ inputField: InputField, rawNumber: String) { internal func updateCardTypeIcon(_ inputField: InputField, rawNumber: String) {
@ -224,9 +255,8 @@ extension InputField {
guard rawNumber.count == cardType.maxLength else { return formatCreditCardNumber(cardType, number: number) } guard rawNumber.count == cardType.maxLength else { return formatCreditCardNumber(cardType, number: number) }
let lastFourDigits = rawNumber.suffix(4) let lastFourDigits = rawNumber.suffix(4)
let maskedSection = String(repeating: "", count: 12) let maskedSection = String(repeating: "", count: 12)
let formattedMaskSection = String.format(maskedSection, indices: cardType.separatorIndices, with: " ") let formattedMaskSection = String.format(maskedSection, indices: cardType.separatorIndices(rawNumber.count), with: " ")
return formattedMaskSection + " " + lastFourDigits return formattedMaskSection + " " + lastFourDigits
} }
} }
} }