Skip to content

Commit

Permalink
Fix issues with phone number validations
Browse files Browse the repository at this point in the history
  • Loading branch information
3lvis committed Jun 14, 2024
1 parent 137da38 commit a2a1bc8
Show file tree
Hide file tree
Showing 6 changed files with 33 additions and 96 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,6 @@ public struct EuropeanPhoneNumberInputValidator: InputValidatable {
}

if valid {
guard let replacementString = replacementString, let range = range else { return false }

let composedString = self.composedString(replacementString, fullString: fullString, inRange: range)
valid = validatePartialEuropeanPhoneNumber(composedString)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,15 @@ public struct MixedPhoneNumberInputValidator: InputValidatable {
self.validation = validation
}

public func validateReplacementString(_ replacementString: String?, fullString: String?, inRange range: NSRange?) -> Bool {
guard let replacementString = replacementString, let range = range else { return false }
public func validateString(_ string: String) -> Bool {
if string.hasPrefix("+") {
return europeanValidator.validateString(string)
} else {
return norwegianValidator.validateString(string)
}
}

public func validateReplacementString(_ replacementString: String?, fullString: String?, inRange range: NSRange?) -> Bool {
let composedString = self.composedString(replacementString, fullString: fullString, inRange: range)

if composedString.hasPrefix("+") {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@ public struct NorwegianPhoneNumberInputValidator: InputValidatable {
}

if valid {
guard let replacementString = replacementString, let range = range else { return false }

let composedString = self.composedString(replacementString, fullString: fullString, inRange: range)
valid = validatePartialNorwegianPhoneNumber(composedString)
}
Expand All @@ -29,34 +27,8 @@ public struct NorwegianPhoneNumberInputValidator: InputValidatable {

private func validatePartialNorwegianPhoneNumber(_ phoneNumber: String) -> Bool {
guard !phoneNumber.isEmpty else { return true }

if phoneNumber.count == 8 {
return validateNorwegianPhoneNumber(phoneNumber)
}

if phoneNumber.hasPrefix("4") {
if phoneNumber.count == 1 {
return true // Just "4" is valid as partial input
} else if phoneNumber.hasPrefix("48") {
return true // Longer sequences must start with "48"
} else {
return false // Any other "4x" is invalid
}
}

let validStartDigits: Set<Character> = ["2", "3", "9"]
if let firstDigit = phoneNumber.first {
return validStartDigits.contains(firstDigit)
}

return false
}

private func validateNorwegianPhoneNumber(_ phoneNumber: String) -> Bool {
let phoneNumberPattern = "^[2349]\\d{7}$|^48\\d{6}$"
let regex = try! NSRegularExpression(pattern: phoneNumberPattern)
let range = NSRange(location: 0, length: phoneNumber.utf16.count)
return regex.firstMatch(in: phoneNumber, options: [], range: range) != nil
return phoneNumber.hasPrefix("4") || phoneNumber.hasPrefix("9")
}

private func composedString(_ replacementString: String?, fullString: String?, inRange range: NSRange?) -> String {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,21 @@ class MixedPhoneNumberInputValidatorTests: XCTestCase {
func testNorwegianPhoneNumbers() {
let validator = MixedPhoneNumberInputValidator()

XCTAssertTrue(validator.validateString("91234567"))
XCTAssertTrue(validator.validateString("48123456"))
XCTAssertTrue(validator.validateString("98765432"))

XCTAssertFalse(validator.validateString("11234567"))
XCTAssertFalse(validator.validateString("28123456"))
XCTAssertFalse(validator.validateString("9876543"))

// Test valid numbers
XCTAssertTrue(validator.validateReplacementString("23456789", fullString: "", inRange: NSRange(location: 0, length: 0)))
XCTAssertTrue(validator.validateReplacementString("91234567", fullString: "", inRange: NSRange(location: 0, length: 0)))
XCTAssertTrue(validator.validateReplacementString("48123456", fullString: "", inRange: NSRange(location: 0, length: 0)))
XCTAssertTrue(validator.validateReplacementString("98765432", fullString: "", inRange: NSRange(location: 0, length: 0))) // Valid number starting with 9

// Test invalid numbers
XCTAssertFalse(validator.validateReplacementString("23456789", fullString: "", inRange: NSRange(location: 0, length: 0)))
XCTAssertFalse(validator.validateReplacementString("12345678", fullString: "", inRange: NSRange(location: 0, length: 0)))
XCTAssertFalse(validator.validateReplacementString("56789012", fullString: "", inRange: NSRange(location: 0, length: 0)))
XCTAssertFalse(validator.validateReplacementString("00000000", fullString: "", inRange: NSRange(location: 0, length: 0)))
Expand All @@ -23,27 +31,27 @@ class MixedPhoneNumberInputValidatorTests: XCTestCase {

// Test partial valid sequences
XCTAssertTrue(validator.validateReplacementString("4", fullString: "", inRange: NSRange(location: 0, length: 0))) // Starts with 4
XCTAssertTrue(validator.validateReplacementString("2", fullString: "", inRange: NSRange(location: 0, length: 0))) // Starts with 2
XCTAssertTrue(validator.validateReplacementString("9", fullString: "", inRange: NSRange(location: 0, length: 0))) // Starts with 9

// Test partial valid sequences (2 characters)
XCTAssertTrue(validator.validateReplacementString("8", fullString: "4", inRange: NSRange(location: 1, length: 0))) // Starts with 4, then 8
XCTAssertTrue(validator.validateReplacementString("1", fullString: "9", inRange: NSRange(location: 1, length: 0))) // Starts with 9, then 1
XCTAssertTrue(validator.validateReplacementString("3", fullString: "2", inRange: NSRange(location: 1, length: 0))) // Starts with 2, then 3
XCTAssertTrue(validator.validateReplacementString("8", fullString: "49", inRange: NSRange(location: 1, length: 0))) // "49" becomes "489", which is valid
XCTAssertTrue(validator.validateReplacementString("9", fullString: "49", inRange: NSRange(location: 2, length: 0))) // "49" becomes "499"
XCTAssertTrue(validator.validateReplacementString("8", fullString: "49", inRange: NSRange(location: 2, length: 0))) // "49" becomes "498"

// Test partial invalid sequences (1 character)
XCTAssertFalse(validator.validateReplacementString("1", fullString: "", inRange: NSRange(location: 0, length: 0))) // Invalid start
XCTAssertFalse(validator.validateReplacementString("5", fullString: "", inRange: NSRange(location: 0, length: 0))) // Invalid start
XCTAssertFalse(validator.validateReplacementString("0", fullString: "", inRange: NSRange(location: 0, length: 0))) // Invalid start
XCTAssertFalse(validator.validateReplacementString("2", fullString: "", inRange: NSRange(location: 0, length: 0))) // Starts with 2

// Test partial invalid sequences (2 characters)
XCTAssertFalse(validator.validateReplacementString("9", fullString: "49", inRange: NSRange(location: 2, length: 0))) // "49" becomes "499", which is invalid
XCTAssertFalse(validator.validateReplacementString("8", fullString: "49", inRange: NSRange(location: 2, length: 0))) // "49" becomes "498", which is invalid
XCTAssertFalse(validator.validateReplacementString("3", fullString: "2", inRange: NSRange(location: 1, length: 0))) // Starts with 2, then 3

// Test valid sequences extended (3 characters)
XCTAssertTrue(validator.validateReplacementString("1", fullString: "481", inRange: NSRange(location: 3, length: 0))) // "481" is a valid sequence
XCTAssertTrue(validator.validateReplacementString("5", fullString: "235", inRange: NSRange(location: 3, length: 0))) // "235" is a valid sequence
XCTAssertFalse(validator.validateReplacementString("5", fullString: "235", inRange: NSRange(location: 3, length: 0))) // "235" is an invalid sequence

// Test other invalid patterns
XCTAssertFalse(validator.validateReplacementString("12345678", fullString: "", inRange: NSRange(location: 0, length: 0))) // Invalid start
Expand All @@ -52,6 +60,15 @@ class MixedPhoneNumberInputValidatorTests: XCTestCase {
func testEuropeanPhoneNumbers() {
let validator = MixedPhoneNumberInputValidator()

XCTAssertTrue(validator.validateString("+447911123456"))
XCTAssertTrue(validator.validateString("+33612345678"))
XCTAssertTrue(validator.validateString("+491711234567"))

XCTAssertFalse(validator.validateString("+336123456789012345"))
XCTAssertFalse(validator.validateString("+0044711123456"))
XCTAssertFalse(validator.validateString("+4479111234567890123456"))
XCTAssertFalse(validator.validateString("+4479"))

// Test valid European numbers
XCTAssertTrue(validator.validateReplacementString("+447911123456", fullString: "", inRange: NSRange(location: 0, length: 0))) // UK
XCTAssertTrue(validator.validateReplacementString("+33612345678", fullString: "", inRange: NSRange(location: 0, length: 0))) // France
Expand Down

This file was deleted.

6 changes: 1 addition & 5 deletions Vanilla/Vanilla/ViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,9 @@ class ViewController: UIViewController {
])
}

var reverse = false

@objc func textFieldDidUpdate() {
let phoneNumber = textField.text ?? ""
textField.backgroundColor = self.inputValidator.validateString(phoneNumber) ? .secondaryLabel : .secondarySystemFill
textField.backgroundColor = self.inputValidator.validateString(phoneNumber) ? .systemMint : .secondarySystemBackground
}
}

Expand All @@ -47,8 +45,6 @@ extension ViewController: UITextFieldDelegate {
return true
}

self.reverse = string.isEmpty

return self.inputValidator.validateReplacementString(string, fullString: textField.text, inRange: range)
}
}
Expand Down

0 comments on commit a2a1bc8

Please sign in to comment.