Skip to content

Commit

Permalink
Fixes for PhoneNumber type determination
Browse files Browse the repository at this point in the history
Several fixes for determining the type of a phone number:

- Determine which metadata record to use based on the number's region,
  not the numeric country code. This fixes using the US metadata for all
  regions using country code 1, for example.
- Adjust the order we check the type regular expressions. The fixed and
  mobile patterns are the most generic so they should be checked last.
- Some fixes for regions with leading zeros
- Fix regex matchesEntirely - add parentheses to the expression

Also update testAllExampleNumbers to check that the expected type is
computed for all example numbers in the metadata.
  • Loading branch information
Eric Fikus committed Mar 25, 2016
1 parent 42b6090 commit 3207eea
Show file tree
Hide file tree
Showing 5 changed files with 107 additions and 27 deletions.
2 changes: 1 addition & 1 deletion PhoneNumberKit/PhoneNumber.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public struct PhoneNumber {
public var type: PhoneNumberType {
get {
let parser = PhoneNumberParser()
let type: PhoneNumberType = parser.checkNumberType(String(nationalNumber), countryCode: countryCode)
let type: PhoneNumberType = parser.checkNumberType(self)
return type
}
}
Expand Down
3 changes: 3 additions & 0 deletions PhoneNumberKit/PhoneNumberKit.swift
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,9 @@ public class PhoneNumberKit: NSObject {
return region.codeID
}
}
if number.leadingZero && parser.checkNumberType("0" + nationalNumber, metadata: region) != .Unknown {
return region.codeID
}
if parser.checkNumberType(nationalNumber, metadata: region) != .Unknown {
return region.codeID
}
Expand Down
51 changes: 30 additions & 21 deletions PhoneNumberKit/PhoneNumberParser.swift
Original file line number Diff line number Diff line change
Expand Up @@ -112,14 +112,23 @@ class PhoneNumberParser {

/**
Check number type (e.g +33 612-345-678 to .Mobile).
- Parameter nationalNumber: National number string.
- Parameter countryCode: International country code (e.g 44 for the UK).
- Returns: Country code is UInt64.
- Parameter phoneNumber: The number to check
- Returns: The type of the number
*/
func checkNumberType(nationalNumber: String, countryCode: UInt64) -> PhoneNumberType {
guard let metadata = self.metadata.metadataPerCode[countryCode] else {
func checkNumberType(phoneNumber: PhoneNumber) -> PhoneNumberType {
guard let region = PhoneNumberKit().regionCodeForNumber(phoneNumber) else {
return .Unknown
}
guard let metadata = metadata.fetchMetadataForCountry(region) else {
return .Unknown
}
if phoneNumber.leadingZero {
let type = checkNumberType("0" + String(phoneNumber.nationalNumber), metadata: metadata)
if type != .Unknown {
return type
}
}
let nationalNumber = String(phoneNumber.nationalNumber)
return checkNumberType(nationalNumber, metadata: metadata)
}

Expand All @@ -130,19 +139,8 @@ class PhoneNumberParser {
if (regex.hasValue(generalNumberDesc.nationalNumberPattern) == false || isNumberMatchingDesc(nationalNumber, numberDesc: generalNumberDesc) == false) {
return .Unknown
}
if (isNumberMatchingDesc(nationalNumber, numberDesc: metadata.fixedLine)) {
if metadata.fixedLine?.nationalNumberPattern == metadata.mobile?.nationalNumberPattern {
return .FixedOrMobile
}
else if (isNumberMatchingDesc(nationalNumber, numberDesc: metadata.mobile)) {
return .FixedOrMobile
}
else {
return .FixedLine
}
}
if (isNumberMatchingDesc(nationalNumber, numberDesc: metadata.mobile)) {
return .Mobile
if (isNumberMatchingDesc(nationalNumber, numberDesc: metadata.pager)) {
return .Pager
}
if (isNumberMatchingDesc(nationalNumber, numberDesc: metadata.premiumRate)) {
return .PremiumRate
Expand All @@ -159,15 +157,26 @@ class PhoneNumberParser {
if (isNumberMatchingDesc(nationalNumber, numberDesc: metadata.personalNumber)) {
return .PersonalNumber
}
if (isNumberMatchingDesc(nationalNumber, numberDesc: metadata.pager)) {
return .Pager
}
if (isNumberMatchingDesc(nationalNumber, numberDesc: metadata.uan)) {
return .UAN
}
if (isNumberMatchingDesc(nationalNumber, numberDesc: metadata.voicemail)) {
return .Voicemail
}
if (isNumberMatchingDesc(nationalNumber, numberDesc: metadata.fixedLine)) {
if metadata.fixedLine?.nationalNumberPattern == metadata.mobile?.nationalNumberPattern {
return .FixedOrMobile
}
else if (isNumberMatchingDesc(nationalNumber, numberDesc: metadata.mobile)) {
return .FixedOrMobile
}
else {
return .FixedLine
}
}
if (isNumberMatchingDesc(nationalNumber, numberDesc: metadata.mobile)) {
return .Mobile
}
return .Unknown
}

Expand Down
2 changes: 1 addition & 1 deletion PhoneNumberKit/RegularExpressions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ class RegularExpressions {
guard var pattern = pattern else {
return false
}
pattern = "^\(pattern)$"
pattern = "^(\(pattern))$"
return matchesExist(pattern, string: string)
}

Expand Down
76 changes: 72 additions & 4 deletions PhoneNumberKitTests/PhoneNumberKitParsingTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -203,13 +203,32 @@ class PhoneNumberKitParsingTests: XCTestCase {
let metaDataArray = PhoneNumberKit().metadata.items.filter{$0.codeID.characters.count == 2}
for metadata in metaDataArray {
let codeID = metadata.codeID
let metaDataDescriptions = [metadata.generalDesc, metadata.fixedLine, metadata.mobile, metadata.tollFree, metadata.premiumRate, metadata.sharedCost, metadata.voip, metadata.voicemail, metadata.pager, metadata.uan, metadata.emergency]
for desc in metaDataDescriptions {
if desc != nil {
if let exampleNumber = desc?.exampleNumber {
let metadataWithTypes: [(MetadataPhoneNumberDesc?, PhoneNumberType?)] = [
(metadata.generalDesc, nil),
(metadata.fixedLine, .FixedLine),
(metadata.mobile, .Mobile),
(metadata.tollFree, .TollFree),
(metadata.premiumRate, .PremiumRate),
(metadata.sharedCost, .SharedCost),
(metadata.voip, .VOIP),
(metadata.voicemail, .Voicemail),
(metadata.pager, .Pager),
(metadata.uan, .UAN),
(metadata.emergency, nil),
]
metadataWithTypes.forEach { record in
if let desc = record.0 {
if let exampleNumber = desc.exampleNumber {
do {
let phoneNumber = try PhoneNumber(rawNumber: exampleNumber, region: codeID)
XCTAssertNotNil(phoneNumber)
if let type = record.1 {
if phoneNumber.type == .FixedOrMobile {
XCTAssert(type == .FixedLine || type == .Mobile)
} else {
XCTAssertEqual(phoneNumber.type, type, "Expected type \(type) for number \(phoneNumber)")
}
}
} catch (let e) {
XCTFail("Failed to create PhoneNumber for \(exampleNumber): \(e)")
}
Expand All @@ -235,6 +254,55 @@ class PhoneNumberKitParsingTests: XCTestCase {
XCTAssertEqual(number.type, PhoneNumberType.TollFree)
}

func testBelizeTollFreeType() {
guard let number = try? PhoneNumber(rawNumber: "08001234123", region: "BZ") else {
XCTFail()
return
}
XCTAssertEqual(number.type, PhoneNumberType.TollFree)
}

func testItalyFixedLineType() {
guard let number = try? PhoneNumber(rawNumber: "0669812345", region: "IT") else {
XCTFail()
return
}
XCTAssertEqual(number.type, PhoneNumberType.FixedLine)
}

func testMaldivesPagerNumber() {
guard let number = try? PhoneNumber(rawNumber: "7812345", region: "MV") else {
XCTFail()
return
}
XCTAssertEqual(number.type, PhoneNumberType.Pager)
}

func testZimbabweVoipType() {
guard let number = try? PhoneNumber(rawNumber: "8686123456", region: "ZW") else {
XCTFail()
return
}
XCTAssertEqual(number.type, PhoneNumberType.VOIP)

}

func testAntiguaPagerNumberType() {
guard let number = try? PhoneNumber(rawNumber: "12684061234") else {
XCTFail()
return
}
XCTAssertEqual(number.type, PhoneNumberType.Pager)
}

func testFranceMobileNumberType() {
guard let number = try? PhoneNumber(rawNumber: "+33 612-345-678") else {
XCTFail()
return
}
XCTAssertEqual(number.type, PhoneNumberType.Mobile)
}

func testPerformanceSimple() {
let numberOfParses = 1000
let startTime = NSDate()
Expand Down

0 comments on commit 3207eea

Please sign in to comment.