Last active
August 27, 2021 18:03
-
-
Save atierian/12e81f1aafa18312109bdb97496c356f to your computer and use it in GitHub Desktop.
Email Data Detector
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // 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