// MARK: DataDetector
class DataDetector {
private class func _find(all type: NSTextCheckingResult.CheckingType,
in string: String, iterationClosure: (String) -> Bool) {
guard let detector = try? NSDataDetector(types: type.rawValue) else { return }
let range = NSRange(string.startIndex ..< string.endIndex, in: string)
let matches = detector.matches(in: string, options: [], range: range)
loop: for match in matches {
for i in 0 ..< match.numberOfRanges {
let nsrange = match.range(at: i)
let startIndex = string.index(string.startIndex, offsetBy: nsrange.lowerBound)
let endIndex = string.index(string.startIndex, offsetBy: nsrange.upperBound)
let range = startIndex..<endIndex
guard iterationClosure(String(string[range])) else { break loop }
}
}
}
class func find(all type: NSTextCheckingResult.CheckingType, in string: String) -> [String] {
var results = [String]()
_find(all: type, in: string) {
results.append($0)
return true
}
return results
}
class func first(type: NSTextCheckingResult.CheckingType, in string: String) -> String? {
var result: String?
_find(all: type, in: string) {
result = $0
return false
}
return result
}
}
// MARK: PhoneNumber
struct PhoneNumber {
private(set) var number: String
init?(extractFrom string: String) {
guard let phoneNumber = PhoneNumber.first(in: string) else { return nil }
self = phoneNumber
}
private init (string: String) { self.number = string }
func makeACall() {
guard let url = URL(string: "tel://\(number.onlyDigits())"),
UIApplication.shared.canOpenURL(url) else { return }
if #available(iOS 10, *) {
UIApplication.shared.open(url)
} else {
UIApplication.shared.openURL(url)
}
}
static func extractAll(from string: String) -> [PhoneNumber] {
DataDetector.find(all: .phoneNumber, in: string)
.compactMap { PhoneNumber(string: $0) }
}
static func first(in string: String) -> PhoneNumber? {
guard let phoneNumberString = DataDetector.first(type: .phoneNumber, in: string) else { return nil }
return PhoneNumber(string: phoneNumberString)
}
}
extension PhoneNumber: CustomStringConvertible { var description: String { number } }
// MARK: String extension
extension String {
// MARK: Get remove all characters exept numbers
func onlyDigits() -> String {
let filtredUnicodeScalars = unicodeScalars.filter { CharacterSet.decimalDigits.contains($0) }
return String(String.UnicodeScalarView(filtredUnicodeScalars))
}
var detectedPhoneNumbers: [PhoneNumber] { PhoneNumber.extractAll(from: self) }
var detectedFirstPhoneNumber: PhoneNumber? { PhoneNumber.first(in: self) }
}
Usage
PhoneNumber(extractFrom: "+1-(800)-123-4567")?.makeACall()
PhoneNumber.extractAll(from: "+1-(800)-123-4567 bla bla 1(617)111-22-33").last?.makeACall()
PhoneNumber.first(in: "+1-(800)-123-4567 bla bla 1(617)111-22-33")?.makeACall()
"+1-(800)-123-4567 bla bla 1(617)111-22-33".detectedPhoneNumbers[1].makeACall()
"+1-(800)-123-4567 bla bla 1(617)111-22-33".detectedFirstPhoneNumber?.makeACall()