Skip to content

Instantly share code, notes, and snippets.

@jstorm31
Last active April 4, 2020 10:15
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jstorm31/cb8148c8887618130302a026a4c2282b to your computer and use it in GitHub Desktop.
Save jstorm31/cb8148c8887618130302a026a4c2282b to your computer and use it in GitHub Desktop.
Swift Czech bank account validation
// Rewritten from PHP code excerpt from https://www.kutac.cz/pocitace-a-internety/jak-poznat-spravne-cislo-uctu
import Foundation
final class Utils {
static let bankCodes: Set = [100, 300, 600, 710, 800, 2010, 2020, 2030, 2060, 2070, 2100, 2200, 2220, 2240, 2250, 2260, 2275, 2600, 2700, 3030, 3050, 3060, 3500, 4000, 4300, 5500, 5800, 6000, 6100, 6200, 6210, 6300, 6700, 6800, 7910, 7940, 7950, 7960, 7970, 7980, 7990, 8030, 8040, 8060, 8090, 8150, 8190, 8198, 8199, 8200, 8215, 8220, 8225, 8230, 8240, 8250, 8255, 8260, 8265, 8270, 8272, 8280, 8283, 8291, 8292, 8293, 8294, 8296]
/// Validates Czech bank account number
static func isValid(bankAccount: String) -> Bool {
let bankAccountRegex = try! NSRegularExpression(pattern: "^(?:([0-9]{1,6})-)?([0-9]{2,10})\\/([0-9]{4})$")
let range = NSRange(location: 0, length: bankAccount.utf16.count)
let match = bankAccountRegex.firstMatch(in: bankAccount, options: [], range: range)
guard match != nil else {
return false
}
let parts = bankAccount.split(separator: "/")
let accountNumber = parts.first!.split(separator: "-")
let bankCode = parts.last!
guard bankCodes.contains(Int(bankCode) ?? 0) else {
return false
}
// Main part
let main = accountNumber.last!.padding(toLength: 10, withPad: "0", startingAt: 0)
guard Utils.bankAccountChecksum(main) % 11 == 0 else {
return false
}
// Optional prefix
if parts.count == 2 {
let prefix = accountNumber.first!.padding(toLength: 10, withPad: "0", startingAt: 0)
guard Utils.bankAccountChecksum(prefix) % 11 == 0 else {
return false
}
}
return true
}
}
private extension Utils {
static func bankAccountChecksum(_ part: String) -> Int {
let weights = [6, 3, 7, 9, 10, 5, 8, 4, 2, 1]
return part.enumerated().reduce(0) { (checksum, item) in
let (index, char) = item
return checksum + weights[index] * (Int(String(char)) ?? 0)
}
}
}
import XCTest
@testable import App
final class UtilsTests: XCTestCase {
func testValidBankAccount() {
XCTAssertTrue(Utils.isValid(bankAccount: "111333/2700"))
XCTAssertTrue(Utils.isValid(bankAccount: "86-199488014/3030"))
}
func testInvalidBankAccount() {
XCTAssertFalse(Utils.isValid(bankAccount: "notBankAccountAtAll"))
XCTAssertFalse(Utils.isValid(bankAccount: "1235234/haha"))
XCTAssertFalse(Utils.isValid(bankAccount: "234534636/2700"))
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment