Created
October 11, 2022 08:53
-
-
Save ksmandersen/9dd7942a9a7c26a1f61dda6583aef24d to your computer and use it in GitHub Desktop.
How to format a credit card number with an (NS)Formatter
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class CreditCardFormatter: Formatter { | |
override func string(for obj: Any?) -> String? { | |
guard let input = obj as? String else { | |
return nil | |
} | |
return formattedCardNumber(input: input) | |
} | |
override func getObjectValue(_ obj: AutoreleasingUnsafeMutablePointer<AnyObject?>?, for string: String, errorDescription error: AutoreleasingUnsafeMutablePointer<NSString?>?) -> Bool { | |
obj?.pointee = string as AnyObject? | |
return true | |
} | |
private func formattedCardNumber(input: String?) -> String? { | |
guard let input = input else { return nil } | |
let stripped = input.replacingOccurrences(of: " ", with: "") | |
// Guess which mask we need to use based on what the user has | |
// typed so far, without any spacing or formatting. | |
let mask = guessFormattingMask(for: stripped) | |
var result = "" | |
var index = stripped.startIndex | |
// Format the stripped input based on the guessed mask | |
for mchar in mask where index < stripped.endIndex { | |
if mchar == "#" { | |
result.append(stripped[index]) | |
index = stripped.index(after: index) | |
} else { | |
if stripped[index] != mchar { | |
result.append(mchar) | |
} else { | |
result.append(stripped[index]) | |
index = stripped.index(after: index) | |
} | |
} | |
} | |
// print("Result: \(result)") | |
// print("Mask: \(mask)") | |
// print("input: \(input.count), result: \(result.count), mask: \(mask.count), stripped: \(stripped.count)") | |
// If we exceeded the mask just append additional characters to the end | |
// of the result | |
if input.count > mask.count && result.endIndex < input.endIndex { | |
result.append(String(input[result.endIndex..<input.endIndex])) | |
} | |
return result | |
} | |
func guessFormattingMask(for string: String) -> String { | |
let defaultMask = "#### #### #### ####" | |
switch string.first { | |
case "3": | |
if string.starts(with: "34") || string.starts(with: "37") { | |
return "#### ###### #####" | |
} else if string.starts(with: "36") { | |
return "#### ##### ####" | |
} else { | |
return defaultMask | |
} | |
case "4": return string.count == 13 ? "#### ##### ####" : defaultMask | |
default: return defaultMask | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment