Skip to content

Instantly share code, notes, and snippets.

@atierian
Last active August 27, 2021 18:03
Show Gist options
  • Select an option

  • Save atierian/12e81f1aafa18312109bdb97496c356f to your computer and use it in GitHub Desktop.

Select an option

Save atierian/12e81f1aafa18312109bdb97496c356f to your computer and use it in GitHub Desktop.
Email Data Detector
// Inspired by https://www.atomicbird.com/blog/data-detection-in-swift/
import Foundation
import XCTest
fileprivate extension URLComponents {
init?(_ url: URL) {
self.init(url: url, resolvingAgainstBaseURL: false)
}
var isMailToScheme: Bool {
scheme == "mailto"
}
}
public extension String {
private static let linkDetector = try? NSDataDetector(types: NSTextCheckingResult.CheckingType.link.rawValue)
/// Extracts valid email address out of a `String` using `NSDataDetector`
/// - Returns: Array of extracted ermail addresses. Empty Array if none found
func extractedEmailAddresses() -> [String] {
// Could go directly from NSCheckingResult to URLComponents to reduce double iteration to single,
// but performance gain would be negligible and this looks prettier :)
String.linkDetector?
.matches(in: self, options: [], range: NSMakeRange(0, count))
.compactMap(\.url)
.compactMap(URLComponents.init)
.filter(\.isMailToScheme)
.map(\.path) ?? []
}
/// Returns true if the `String` contains one or more valid email addresses
var containsEmailAddress: Bool {
!extractedEmailAddresses().isEmpty
}
/// Returns true if `String` is a valid email address and contains no other text
var isEmailAddress: Bool {
extractedEmailAddresses().first == self
}
}
// MARK: Tests
class EmailDetectorTests: XCTestCase {
func testSingleEmail() {
let singleEmail = "[email protected]"
XCTAssertEqual(singleEmail.extractedEmailAddresses(), [singleEmail])
XCTAssertTrue(singleEmail.containsEmailAddress)
XCTAssertTrue(singleEmail.isEmailAddress)
}
func testMultipleEmails() {
let multipleEmails = "[email protected] and [email protected]"
XCTAssertEqual(multipleEmails.extractedEmailAddresses(), ["[email protected]", "[email protected]"])
XCTAssertEqual(multipleEmails.extractedEmailAddresses().count, 2)
XCTAssertTrue(multipleEmails.containsEmailAddress)
XCTAssertFalse(multipleEmails.isEmailAddress)
}
func testNoEmails() {
let noEmails = "some long string that doesn't contain an email address"
XCTAssertTrue(noEmails.extractedEmailAddresses().isEmpty)
XCTAssertFalse(noEmails.containsEmailAddress)
XCTAssertFalse(noEmails.isEmailAddress)
}
}
EmailDetectorTests.defaultTestSuite.run()
// MARK: Examples
var foo = "[email protected]"
print(foo.extractedEmailAddresses()) // ["[email protected]"]
print(foo.containsEmailAddress) // true
print(foo.isEmailAddress) // true
foo = "[email protected] and [email protected]"
print(foo.extractedEmailAddresses()) // ["[email protected]", "[email protected]"]
print(foo.containsEmailAddress) // true
print(foo.isEmailAddress) // false
foo = "some long string that doesn't contain an email address"
print(foo.extractedEmailAddresses()) // []
print(foo.containsEmailAddress) // false
print(foo.isEmailAddress) // false
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment