// // Copyright 2021 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only // import Foundation import SignalCoreKit import SignalServiceKit public struct RegistrationCountryState: Equatable, Dependencies { // e.g. France public let countryName: String // e.g. +33 public let callingCode: String // e.g. FR public let countryCode: String public init(countryName: String, callingCode: String, countryCode: String) { self.countryName = countryName self.callingCode = callingCode self.countryCode = countryCode } public static var defaultValue: RegistrationCountryState { AssertIsOnMainThread() let countryCode: String = PhoneNumberUtil.defaultCountryCode() let callingCodeNumber: NSNumber = phoneNumberUtil.getCallingCode(forRegion: countryCode) let callingCode = "\(COUNTRY_CODE_PREFIX)\(callingCodeNumber)" let countryName = PhoneNumberUtil.countryName(fromCountryCode: countryCode) return RegistrationCountryState(countryName: countryName, callingCode: callingCode, countryCode: countryCode) } // MARK: - public static func countryState(forE164 e164: String) -> RegistrationCountryState? { for countryState in allCountryStates { if e164.hasPrefix(countryState.callingCode) { return countryState } } return nil } public static var allCountryStates: [RegistrationCountryState] { RegistrationCountryState.buildCountryStates(searchText: nil) } public static func buildCountryStates(searchText: String?) -> [RegistrationCountryState] { let searchText = searchText?.strippedOrNil let countryCodes: [String] = NSObject.phoneNumberUtil.countryCodes(forSearchTerm: searchText) return RegistrationCountryState.buildCountryStates(countryCodes: countryCodes) } public static func buildCountryStates(countryCodes: [String]) -> [RegistrationCountryState] { return countryCodes.compactMap { (countryCode: String) -> RegistrationCountryState? in guard let countryCode = countryCode.strippedOrNil else { owsFailDebug("Invalid countryCode.") return nil } guard let callingCode = NSObject.phoneNumberUtil.callingCode(fromCountryCode: countryCode)?.strippedOrNil else { owsFailDebug("Invalid callingCode.") return nil } guard let countryName = PhoneNumberUtil.countryName(fromCountryCode: countryCode).strippedOrNil else { owsFailDebug("Invalid countryName.") return nil } guard callingCode != "+0" else { owsFailDebug("Invalid callingCode.") return nil } return RegistrationCountryState( countryName: countryName, callingCode: callingCode, countryCode: countryCode ) } } } // MARK: - public struct RegistrationPhoneNumber { public let countryState: RegistrationCountryState public let nationalNumber: String public let e164: E164? public init(countryState: RegistrationCountryState, nationalNumber: String) { self.countryState = countryState self.nationalNumber = nationalNumber self.e164 = E164("\(countryState.callingCode)\(nationalNumber)") } } public class RegistrationPhoneNumberParser { private let phoneNumberUtil: PhoneNumberUtil public init(phoneNumberUtil: PhoneNumberUtil) { self.phoneNumberUtil = phoneNumberUtil } public func parseE164(_ phoneNumber: E164) -> RegistrationPhoneNumber? { guard let countryState = RegistrationCountryState.countryState(forE164: phoneNumber.stringValue), let phoneNumber = phoneNumberUtil.parseE164(phoneNumber) else { return nil } let nationalNumber = phoneNumberUtil.nationalNumber(for: phoneNumber) return RegistrationPhoneNumber(countryState: countryState, nationalNumber: nationalNumber) } }