Skip to content

Instantly share code, notes, and snippets.

@unnamedd
Forked from cwagdev/CreditCard.swift
Last active August 30, 2016 06:44
Show Gist options
  • Save unnamedd/70521a1e8f59b056988e9b3b2eb6c76c to your computer and use it in GitHub Desktop.
Save unnamedd/70521a1e8f59b056988e9b3b2eb6c76c to your computer and use it in GitHub Desktop.
Credit Cards represented in Swift
//
// Created by Chris Wagner
// https://gist.github.com/cwagdev/e66d4806c1f63fe9387a
//
// Modified by Thiago Holanda (August/2016)
//
// These modifications was made to support the latest version of Swift
// before the major version (3.0) is released. This code will compile
// without any problem on Swift 2.2.
//
// If you have any big issue, please, contact the original author or
// contact me to talk about this specific modification at twitter.com/tholanda
/// Describes a type of credit card for a subset of known types.
enum CreditCardType: String, CustomStringConvertible {
case Amex = "Amex"
case DinersClub = "Diners"
case Discover = "Discover"
case JCB = "JCB"
case MasterCard = "MasterCard"
case Visa = "Visa"
case Unknown = "Unknown"
var description: String {
return self.rawValue
}
/// A set of lengths that are considered valid for the type
var validLengths: Set<Int> {
switch self {
case .Amex:
return [15]
case .DinersClub:
return [14, 15, 16]
case .Discover:
return [16]
case .JCB:
return [16]
case .MasterCard:
return [16]
case .Visa:
return [13, 16]
case .Unknown:
return []
}
}
init(number: String) {
if let first = number.prefix(1) where first == 4 {
self = .Visa
return
}
if let firstTwo = number.prefix(2) {
if firstTwo == 35 {
self = .JCB
return
}
if [30, 36, 38, 39].contains(firstTwo) {
self = .DinersClub
return
}
if 50...55 ~= firstTwo {
self = .MasterCard
return
}
if firstTwo == 34 || firstTwo == 37 {
self = .Amex
return
}
if firstTwo == 65 {
self = .Discover
return
}
}
if let firstThree = number.prefix(3) where 644...649 ~= firstThree {
self = .Discover
return
}
if let firstFour = number.prefix(4) where firstFour == 6011 {
self = .Discover
return
}
if let firstSix = number.prefix(6) where 622126...622925 ~= firstSix {
self = .Discover
return
}
if let firstFour = number.prefix(4) where 3528...3589 ~= firstFour {
self = .JCB
return
}
self = .Unknown
}
}
extension String {
/**
The first `length` characters of the String as an Int.
:param: length The number of characters to return
:returns: The first `length` characters of the String as an Int. `nil` if `length` exceed the length of the String or is not representable as Int.
*/
func prefix(length: Int) -> Int? {
let range = startIndex..<startIndex.advancedBy(length)
return Int(self.substringWithRange(range))
}
}
/// Represents a String as a Credit Card
struct CreditCard {
init(card: String) {
self.number = card.stringByReplacingOccurrencesOfString(" ", withString: "")
}
/// The credit card number represented as a String
var number: String
/// The type of credit card, this is generally accurate once the first two numbers are provided
var type: CreditCardType { return CreditCardType(number: number) }
/// The last 4 numbers of the card, nil if the length is < 4
var last4: String? {
if number.characters.count < 4 {
return nil
}
let range = number.endIndex.advancedBy(-4)..<number.endIndex
return number.substringWithRange(range)
}
/// A display version of the credit card number
var formattedString: String {
return number
}
/// True when both `isValidLength` and `isValidLuhn` are true
var isValid: Bool {
return isValidLength // && isValidLuhn
}
/// True when the length of the card number meets a required length for the card type
var isValidLength: Bool {
return type.validLengths.contains(number.characters.count)
}
/// True when the Luhn algorithm https://en.wikipedia.org/wiki/Luhn_algorithm succeeds
var isValidLuhn: Bool {
var sum = 0
let digitStrings = number.characters.reverse().map { Int(String($0)) }
for tuple in digitStrings.enumerate() {
if let digit = tuple.element {
let odd = tuple.index % 2 == 1
switch (odd, digit) {
case (true, 9):
sum += 9
case (true, 0...8):
sum += (digit * 2) % 9
default:
sum += digit
}
} else {
return false
}
}
return sum % 10 == 0
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment