IBAN Validator Swift
I am writing an algorithm to check IBAN (International Bank Account Number) in Swift 3 and cannot figure out one of the validations.
IBAN example - BE68539007547034
Here are the validation rules -
- The input number must be 16 in length.
- The first 2 characters are the country code (not numeric).
- The last 14 are numeric.
- The last 2 symbols are the result modulo 97 of the previous 12 digital symbols.
While # 1 - # 3 is clear, I need clarity in # 4. If anyone has done this before and found out about it, please let me know.
source to share
From Wikipedia
let IBAN = "GB82WEST12345698765432" // uppercase, no whitespace !!!!
var a = IBAN.utf8.map{ $0 }
while a.count < 4 {
a.append(0)
}
let b = a[4..<a.count] + a[0..<4]
let c = b.reduce(0) { (r, u) -> Int in
let i = Int(u)
return i > 64 ? (100 * r + i - 55) % 97: (10 * r + i - 48) % 97
}
print( "IBAN \(IBAN) is", c == 1 ? "valid": "invalid")
seal
IBAN GB82WEST12345698765432 is valid
With the IBAN from your question it prints
IBAN BE68539007547034 is valid
source to share
The validation algorithm is pretty simple if you follow the algorithm on Wikipedia :
extension String {
private func mod97() -> Int {
let symbols: [Character] = Array(self)
let swapped = symbols.dropFirst(4) + symbols.prefix(4)
let mod: Int = swapped.reduce(0) { (previousMod, char) in
let value = Int(String(char), radix: 36)! // "0" => 0, "A" => 10, "Z" => 35
let factor = value < 10 ? 10 : 100
return (factor * previousMod + value) % 97
}
return mod
}
func passesMod97Check() -> Bool {
guard self.characters.count >= 4 else {
return false
}
let uppercase = self.uppercased()
guard uppercase.range(of: "^[0-9A-Z]*$", options: .regularExpression) != nil else {
return false
}
return (uppercase.mod97() == 1)
}
}
Using:
let iban = "XX0000000..."
let valid = iban.passesMod97Check()
If you want to check the format for a specific country, just change the regex like
"^[A-Z]{2}[0-9]{14}$"
or directly
"^BE\\d{14}$"
source to share
I found a great solution that works for me in Objective-C https://gist.github.com/0xc010d/5301790 you can rewrite for Swift or use bridge header. Objective-C implementation of the mod97 IBAN verification algorithm
#import <Foundation/Foundation.h>
@interface NSString (Mod97Check)
- (BOOL)passesMod97Check; // Returns result of mod 97 checking algorithm. Might be used to check IBAN.
// Expects string to contain digits and/or upper-/lowercase letters; space and all the rest symbols are not acceptable.
@end
#import "NSString+Mod97Check.h"
@implementation NSString (Mod97Check)
- (BOOL)passesMod97Check {
NSString *string = [self uppercaseString];
NSInteger mod = 0, length = [self length];
for (NSInteger index = 4; index < length + 4; index ++) {
unichar character = [string characterAtIndex:index % length];
if (character >= '0' && character <= '9') {
mod = (10 * mod + (character - '0')) % 97; // '0'=>0, '1'=>1, ..., '9'=>9
}
else if (character >= 'A' && character <= 'Z') {
mod = (100 * mod + (character - 'A' + 10)) % 97; // 'A'=>10, 'B'=>11, ..., 'Z'=>35
}
else {
return NO;
}
}
return (mod == 1);
}
@end
-(BOOL)isValidIBAN {
NSString *iban = self;
static NSString* const LettersAndDecimals = @"ABCDEFGHIJKLKMNOPQRSTUVWXYZ0123456789";
iban = [[iban stringByReplacingOccurrencesOfString:@" " withString:@""] uppercaseString];
NSCharacterSet *invalidChars = [[NSCharacterSet characterSetWithCharactersInString:LettersAndDecimals] invertedSet];
if ([iban rangeOfCharacterFromSet:invalidChars].location != NSNotFound)
{
return NO;
}
int checkDigit = [iban substringWithRange:NSMakeRange(2, 2)].intValue;
iban = [NSString stringWithFormat:@"%@%@",[iban substringWithRange:NSMakeRange(4, iban.length - 4)], [iban substringWithRange:NSMakeRange(0, 4)]] ;
for (int i = 0; i < iban.length; i++) {
unichar c = [iban characterAtIndex:i];
if (c >= 'A' && c <= 'Z') {
iban = [NSString stringWithFormat:@"%@%d%@", [iban substringWithRange:NSMakeRange(0, i)], (c - 'A' + 10),[iban substringWithRange:NSMakeRange(i+1, iban.length - i - 1)]];
}
}
iban = [[iban substringWithRange:NSMakeRange(0, iban.length - 2)] stringByAppendingString:@"00"];
while(true)
{
int iMin = (int)MIN(iban.length, 9);
NSString* strPart = [iban substringWithRange:NSMakeRange(0, iMin)];
int decnumber = strPart.intValue;
if(decnumber < 97 || iban.length < 3)
break;
int del = decnumber % 97;
iban = [NSString stringWithFormat:@"%d%@", del, [iban substringFromIndex:iMin]];
}
int check = 98 - iban.intValue;
return checkDigit == check;
}
source to share