├── _config.yml
├── Resources
└── hero.png
├── TestGround-tvOS.playground
├── contents.xcplayground
└── Contents.swift
├── TestGround-iOS.playground
├── playground.xcworkspace
│ └── contents.xcworkspacedata
├── contents.xcplayground
└── Contents.swift
├── TestGround-macOS.playground
├── playground.xcworkspace
│ └── contents.xcworkspacedata
├── contents.xcplayground
└── Contents.swift
├── MKDataDetector.xcodeproj
├── project.xcworkspace
│ └── contents.xcworkspacedata
├── xcshareddata
│ └── xcschemes
│ │ ├── MKDataDetector.xcscheme
│ │ └── MKDataDetectorTests.xcscheme
└── project.pbxproj
├── MKDataDetector.xcworkspace
├── xcshareddata
│ ├── WorkspaceSettings.xcsettings
│ └── IDEWorkspaceChecks.plist
└── contents.xcworkspacedata
├── .travis.yml
├── MKDataDetectorTests
├── TestUtilities.swift
├── Info.plist
├── TransitInformationExtensionTests.swift
├── DateExtensionTests.swift
├── PhoneNumberExtensionTests.swift
├── LinkExtensionTests.swift
├── MultiDataTests.swift
├── StringAccessTests.swift
└── AddressExtensionTests.swift
├── .gitignore
├── MKDataDetector
├── MKDataDetector.h
├── ResultType.swift
├── Info.plist
├── AnalysisResult.swift
├── Link.swift
├── PhoneNumber.swift
├── TransitInformation.swift
├── MultiData.swift
├── AliasManager.swift
├── CoreKeys.swift
├── UtilityExtensions.swift
├── Address.swift
├── StringAccess.swift
├── Date.swift
└── MKDataDetectorService.swift
├── CHANGELOG.md
├── MKDataDetector.podspec
├── CONTRIBUTING.md
├── LICENSE.md
├── CODE_OF_CONDUCT.md
└── README.md
/_config.yml:
--------------------------------------------------------------------------------
1 | theme: jekyll-theme-cayman
--------------------------------------------------------------------------------
/Resources/hero.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mayankk2308/mkdatadetector/HEAD/Resources/hero.png
--------------------------------------------------------------------------------
/TestGround-tvOS.playground/contents.xcplayground:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/TestGround-iOS.playground/playground.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/TestGround-macOS.playground/playground.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/TestGround-iOS.playground/contents.xcplayground:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/TestGround-macOS.playground/contents.xcplayground:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/MKDataDetector.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/MKDataDetector.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: objective-c
2 | osx_image: xcode10.2
3 | xcode_project: MKDataDetector.xcodeproj
4 | xcode_scheme: MKDataDetector
5 | script: xcodebuild clean build -sdk iphonesimulator -workspace MKDataDetector.xcworkspace -scheme MKDataDetector CODE_SIGNING_REQUIRED=NO
6 |
--------------------------------------------------------------------------------
/MKDataDetector.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/MKDataDetectorTests/TestUtilities.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TestUtilities.swift
3 | // MKDataDetector
4 | //
5 | // Created by Jeet Parte on 22/07/17.
6 | // Copyright 2017 Mayank Kumar. Available under the MIT license.
7 | //
8 |
9 | import Foundation
10 |
11 | func concatenate(_ stringArray: [String]) -> String {
12 | var string = ""
13 | for item in stringArray {
14 | string += item + ". "
15 | }
16 | return string
17 | }
18 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Xcode
2 | #
3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
4 |
5 | ## Build generated
6 | build/
7 | DerivedData/
8 |
9 | ## Various settings
10 | *.pbxuser
11 | !default.pbxuser
12 | *.mode1v3
13 | !default.mode1v3
14 | *.mode2v3
15 | !default.mode2v3
16 | *.perspectivev3
17 | !default.perspectivev3
18 | xcuserdata/
19 |
20 | ## Other
21 | *.moved-aside
22 | *.xccheckout
23 | *.xcscmblueprint
24 | *.DS_Store
25 |
--------------------------------------------------------------------------------
/MKDataDetector.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
12 |
13 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/MKDataDetector/MKDataDetector.h:
--------------------------------------------------------------------------------
1 | //
2 | // MKDataDetector.h
3 | // MKDataDetector
4 | //
5 | // Created by Mayank Kumar on 7/8/17.
6 | // Copyright 2017 Mayank Kumar. Available under the MIT license.
7 | //
8 |
9 | #import
10 |
11 | //! Project version number for MKDataDetector.
12 | FOUNDATION_EXPORT double MKDataDetectorVersionNumber;
13 |
14 | //! Project version string for MKDataDetector.
15 | FOUNDATION_EXPORT const unsigned char MKDataDetectorVersionString[];
16 |
17 | // In this header, you should import all the public headers of your framework using statements like #import
18 |
19 |
20 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # 3.0.0
2 | * Conforms to Swift **5**.
3 | * Adds support for **tvOS** and **watchOS**.
4 |
5 | # 2.0.2
6 | * Conforms to Swift **4.2**.
7 |
8 | # 2.0.1
9 | * Conforms to Swift **4.1**.
10 |
11 | # 2.0.0
12 | * Migrates to Swift 4.0.
13 | * Removes ability to add to calendar.
14 | * Adds ability to generate `EKEvent` objects instead.
15 | * More concise documentation.
16 |
17 | # 1.0.3
18 | Change Swift version to 3.0 to attempt **CocoaDocs** resolution.
19 |
20 | # 1.0.2
21 | * Documentation update.
22 | * Removal of **(c)** symbol from **swift** files.
23 |
24 | # 1.0.1
25 | Minor documentation changes and **CocoaPods** page fix.
26 |
27 | # 1.0.0
28 | Initial stable release.
29 |
--------------------------------------------------------------------------------
/MKDataDetector/ResultType.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ResultType.swift
3 | // MKDataDetector
4 | //
5 | // Created by Mayank Kumar on 7/11/17.
6 | // Copyright 2017 Mayank Kumar. Available under the MIT license.
7 | //
8 |
9 | /// Defines the types of detectable results that are available.
10 | public enum ResultType {
11 |
12 | /// Attempts to locate dates.
13 | case date
14 |
15 | /// Attempts to locate addresses.
16 | case address
17 |
18 | /// Attempts to locate URL links.
19 | case link
20 |
21 | /// Attempts to locate phone numbers.
22 | case phoneNumber
23 |
24 | /// Attempts to locate transit information.
25 | case transitInformation
26 | }
27 |
--------------------------------------------------------------------------------
/MKDataDetectorTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 |
22 |
23 |
--------------------------------------------------------------------------------
/MKDataDetector/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | $(MARKETING_VERSION)
19 | CFBundleVersion
20 | 1
21 | NSPrincipalClass
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/MKDataDetector.podspec:
--------------------------------------------------------------------------------
1 | Pod::Spec.new do |spec|
2 | spec.name = 'MKDataDetector'
3 | spec.version = '3.0.0'
4 | spec.watchos.deployment_target = '2.0'
5 | spec.tvos.deployment_target = '9.0'
6 | spec.ios.deployment_target = '8.0'
7 | spec.osx.deployment_target = '10.9'
8 | spec.license = { :type => 'MIT', :file => 'LICENSE.md' }
9 | spec.source = { :git => 'https://github.com/mayankk2308/mkdatadetector.git', :tag => spec.version.to_s}
10 | spec.authors = {'Mayank Kumar' => 'mayankk2308@gmail.com', 'Jeet Parte' => 'jeetparte@gmail.com'}
11 | spec.homepage = 'https://mayankk2308.github.io/mkdatadetector/'
12 | spec.summary = 'A Swift wrapper for NSDataDetector that simplifies detection and application of dates, links, addresses, etc. from natural language text.'
13 | spec.source_files = 'MKDataDetector/*.swift'
14 | spec.swift_version = '4.2'
15 | end
16 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | When contributing to this repository, please first discuss the change you wish to make via the **Issues** section. For any interactions, please follow the outlined **Code of Conduct**.
4 |
5 | ## Issues, Requests, & Suggestions
6 | Before creating an issue or requesting features, please check the Issues section if it has already been filed. Please avoid filing duplicate requests.
7 |
8 | ## Pull Request Process
9 |
10 | 1. Ensure any install or build dependencies are removed before the end of the layer when doing a build.
11 | 2. Update the README.md with details of changes to the interface, this includes new environment variables, exposed ports, useful file locations and container parameters.
12 | 3. Increase the version numbers in any examples files and the README.md to the new version that this Pull Request would represent. The versioning scheme we use is [Semantic Versioning](http://semver.org/) from 1.0.0 onwards. Pre-releases may not adhere to this scheme.
13 | 4. We will review all pull requests and merge them accordingly.
14 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Mayank Kumar
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/MKDataDetector/AnalysisResult.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AnalysisResult.swift
3 | // MKDataDetector
4 | //
5 | // Created by Mayank Kumar on 7/10/17.
6 | // Copyright 2017 Mayank Kumar. Available under the MIT license.
7 | //
8 |
9 | /// Defines types that may be retrieved as analysis result data.
10 | public protocol AnalysisData {}
11 |
12 | extension String: AnalysisData {}
13 | extension Date: AnalysisData {}
14 | extension URL: AnalysisData {}
15 | extension Dictionary: AnalysisData {}
16 |
17 | /// A generic struct used to describe items located by text checking. Each `AnalysisResult` represents an occurence of requested textual content found during analysis of a body of text.
18 | public struct AnalysisResult {
19 |
20 | /// The entire text body in which the matched substring was contained.
21 | public var source: String
22 |
23 | /// The range of the matched portion of the text body.
24 | public var rangeInSource: NSRange
25 |
26 | /// The substring corresponding to the matched portion of the text body.
27 | public var dataString: String
28 |
29 | /// The `ResultType` corresponding to the data being returned.
30 | public var dataType: ResultType
31 |
32 | /// The data extracted from the source input.
33 | public var data: T
34 | }
35 |
--------------------------------------------------------------------------------
/MKDataDetector/Link.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Link.swift
3 | // MKDataDetector
4 | //
5 | // Created by Mayank Kumar on 7/9/17.
6 | // Copyright 2017 Mayank Kumar. Available under the MIT license.
7 | //
8 |
9 | import Foundation
10 |
11 | extension MKDataDetectorService {
12 |
13 | /// Retrieves URL links matched from a given body of text along with the range of each substring that matched.
14 | ///
15 | /// - Parameter textBody: A body of natural language text.
16 | /// - Returns: An array of `URLAnalysisResult` instances or `nil` if no links could be found.
17 | public func extractLinks(fromTextBody textBody: String) -> [URLAnalysisResult]? {
18 | return extractData(fromTextBody: textBody, withResultTypes: [.link])
19 | }
20 |
21 | /// Retrieves URL links matched from the given bodies of text along with the range of each substring that matched.
22 | ///
23 | /// - Parameter textBodies: A set of multiple bodies of natural language text.
24 | /// - Returns: An array of `[URLAnalysisResult]` instances or `nil` if no links could be found.
25 | /// - Note: Inside the returned array, a non-empty `[URLAnalysisResult]` instance is returned for each text body which contains a match.
26 | public func extractLinks(fromTextBodies textBodies: [String]) -> [[URLAnalysisResult]]? {
27 | return extractData(fromTextBodies: textBodies, withResultTypes: [.link])
28 | }
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/MKDataDetector/PhoneNumber.swift:
--------------------------------------------------------------------------------
1 | //
2 | // PhoneNumber.swift
3 | // MKDataDetector
4 | //
5 | // Created by Mayank Kumar on 7/9/17.
6 | // Copyright 2017 Mayank Kumar. Available under the MIT license.
7 | //
8 |
9 | import Foundation
10 |
11 | extension MKDataDetectorService {
12 |
13 | /// Retrieves phone numbers matched from a given body of text along with the range of each substring that matched.
14 | ///
15 | /// - Parameter textBody: A body of natural language text.
16 | /// - Returns: An array of `PhoneNumberAnalysisResult` instances or `nil` if no phone numbers could be found.
17 | public func extractPhoneNumbers(fromTextBody textBody: String) -> [PhoneNumberAnalysisResult]? {
18 | return extractData(fromTextBody: textBody, withResultTypes: [.phoneNumber])
19 | }
20 |
21 | /// Retrieves phone numbers matched from the given bodies of text along with the range of each substring that matched.
22 | ///
23 | /// - Parameter textBodies: A set of multiple bodies of natural language text.
24 | /// - Returns: An array of `[PhoneNumberAnalysisResult]` instances or `nil` if no phone numbers could be found.
25 | /// - Note: Inside the returned array, a non-empty `[PhoneNumberAnalysisResult]` instance is returned for each text body which contains a match.
26 | public func extractPhoneNumbers(fromTextBodies textBodies: [String]) -> [[PhoneNumberAnalysisResult]]? {
27 | return extractData(fromTextBodies: textBodies, withResultTypes: [.phoneNumber])
28 | }
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/MKDataDetector/TransitInformation.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TransitInformation.swift
3 | // MKDataDetector
4 | //
5 | // Created by Mayank Kumar on 7/9/17.
6 | // Copyright 2017 Mayank Kumar. Available under the MIT license.
7 | //
8 |
9 | import Foundation
10 |
11 | extension MKDataDetectorService {
12 |
13 | /// Retrieves transit information matched from a given body of text along with the range of each substring that matched.
14 | ///
15 | /// - Parameter textBody: A body of natural language text.
16 | /// - Returns: An array of `TransitAnalysisResult` instances or `nil` if no transit information could be found.
17 | public func extractTransitInformation(fromTextBody textBody: String) -> [TransitAnalysisResult]? {
18 | return extractData(fromTextBody: textBody, withResultTypes: [.transitInformation])
19 | }
20 |
21 | /// Retrieves transit information matched from the given bodies of text along with the range of each substring that matched.
22 | ///
23 | /// - Parameter textBodies: A set of multiple bodies of natural language text.
24 | /// - Returns: An array of `[TransitAnalysisResult]` instances or `nil` if no transit information could be found.
25 | /// - Note: Inside the returned array, a non-empty `[TransitAnalysisResult]` instance is returned for each text body which contains a match.
26 | public func extractTransitInformation(fromTextBodies textBodies: [String]) -> [[TransitAnalysisResult]]? {
27 | return extractData(fromTextBodies: textBodies, withResultTypes: [.transitInformation])
28 | }
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/MKDataDetector/MultiData.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MultiData.swift
3 | // MKDataDetector
4 | //
5 | // Created by Mayank Kumar on 7/17/17.
6 | // Copyright 2017 Mayank Kumar. Available under the MIT license.
7 | //
8 |
9 | import Foundation
10 |
11 | extension MKDataDetectorService {
12 |
13 | /// Retrieves multiple types of information from a given body of text along with the range of each substring that matched.
14 | ///
15 | /// - Parameters:
16 | /// - textBody: A body of natural language text.
17 | /// - types: The types of data to detect.
18 | /// - Returns: An array of `GenericAnalysisResult` instances or `nil` if matches could not be found.
19 | public func extractInformation(fromTextBody textBody: String, withResultTypes types: ResultType ...) -> [GenericAnalysisResult]? {
20 | return types.isEmpty ? nil : extractData(fromTextBody: textBody, withResultTypes: types)
21 | }
22 |
23 | /// Retrieves multiple types of information from the given bodies of text along with the range of each substring that matched.
24 | ///
25 | /// - Parameters:
26 | /// - textBodies: An array of multiple bodies of natural language text.
27 | /// - types: The types of data to detect.
28 | /// - Returns: An array of `[GenericAnalysisResult]` instances or `nil` if matches could not be found.
29 | /// - Note: Inside the returned array, a non-empty `[GenericAnalysisResult]` instance is returned for each text body which contains a match.
30 | public func extractInformation(fromTextBodies textBodies: [String], withResultTypes types: ResultType ...) -> [[GenericAnalysisResult]]? {
31 | return types.isEmpty ? nil : extractData(fromTextBodies: textBodies, withResultTypes: types)
32 | }
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/MKDataDetector/AliasManager.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AliasManager.swift
3 | // MKDataDetector
4 | //
5 | // Created by Mayank Kumar on 7/19/17.
6 | // Copyright 2017 Mayank Kumar. Available under the MIT License.
7 | //
8 |
9 | import CoreLocation
10 |
11 | /// An address dictionary returned as part of an `AddressAnalysisResult`.
12 | public typealias AddressInfo = Dictionary
13 |
14 | /// A transit dictionary returned as part of a `TransitAnalysisResult`.
15 | public typealias TransitInfo = Dictionary
16 |
17 | /// An `AnalysisResult` that represents an occurence of a phone number found during analysis of a body of text.
18 | public typealias PhoneNumberAnalysisResult = AnalysisResult
19 |
20 | /// An `AnalysisResult` that represents an occurence of a date found during analysis of a body of text.
21 | public typealias DateAnalysisResult = AnalysisResult
22 |
23 | /// An `AnalysisResult` that represents an occurence of a URL found during analysis of a body of text.
24 | public typealias URLAnalysisResult = AnalysisResult
25 |
26 | /// An `AnalysisResult` that represents an occurence of an address component found during analysis of a body of text.
27 | public typealias AddressAnalysisResult = AnalysisResult
28 |
29 | /// An `AnalysisResult` that represents an occurence of transit information found during analysis of a body of text.
30 | public typealias TransitAnalysisResult = AnalysisResult
31 |
32 | /// An `AnalysisResult` that represents an occurance of any information type that conforms to `AnalysisData`.
33 | public typealias GenericAnalysisResult = AnalysisResult
34 |
35 | /// A completion handler for determining success or failure of a task.
36 | public typealias successCompletion = (Bool) -> Void
37 |
38 | /// A completion handler that calls with a `CLLocation` object or `nil` if the object was not found.
39 | public typealias locationCompletion = (CLLocation?) -> Void
40 |
--------------------------------------------------------------------------------
/MKDataDetectorTests/TransitInformationExtensionTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TransitInformationExtensionTests.swift
3 | // MKDataDetector
4 | //
5 | // Created by Jeet Parte on 23/07/17.
6 | // Copyright 2017 Mayank Kumar. Available under the MIT license.
7 | //
8 |
9 | import XCTest
10 | @testable import MKDataDetector
11 |
12 | class TransitInformationTests: XCTestCase {
13 |
14 | let dataDetectorService = MKDataDetectorService()
15 | var textBody = "UA 2392"
16 | var textBodies = ["Flight 2334", "EK 239 to Boston", "Emirates Airlines", "United Flight 2223"]
17 | let type: NSTextCheckingResult.CheckingType = .transitInformation
18 | var nsDataDetector: NSDataDetector!
19 |
20 | override func setUp() {
21 | super.setUp()
22 | nsDataDetector = try! NSDataDetector(types: type.rawValue)
23 | }
24 |
25 | func testSingleTextBody() {
26 | let results = dataDetectorService.extractTransitInformation(fromTextBody: textBody)
27 | let expectedCount = nsDataDetector.numberOfMatches(in: textBody, range: NSMakeRange(0, textBody.utf16.count))
28 | if expectedCount > 0 {
29 | XCTAssertNotNil(results)
30 | XCTAssertEqual(results!.count, expectedCount)
31 | } else {
32 | XCTAssertNil(results)
33 | }
34 | }
35 |
36 | func testMultipleTextBodies() {
37 | let combinedResults = dataDetectorService.extractTransitInformation(fromTextBodies: textBodies)
38 |
39 | //Check no.of matches for all textbodies
40 | let mergedTextBody = concatenate(textBodies)
41 | let totalMatches = nsDataDetector.numberOfMatches(in: mergedTextBody, range: NSMakeRange(0, mergedTextBody.utf16.count))
42 | if totalMatches > 0 {
43 | XCTAssertNotNil(combinedResults)
44 | for individualResult in combinedResults! {
45 | XCTAssertFalse(individualResult.isEmpty)
46 |
47 | //Check no. of matches for a present textbody
48 | let source = individualResult.first!.source //grab the textbody
49 | let expectedCount = nsDataDetector.numberOfMatches(in: source, range: NSMakeRange(0, source.utf16.count))
50 | XCTAssertEqual(individualResult.count, expectedCount)
51 | }
52 | } else {
53 | XCTAssertNil(combinedResults)
54 | }
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/MKDataDetector/CoreKeys.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CoreKeys.swift
3 | // MKDataDetector
4 | //
5 | // Created by Mayank Kumar on 7/11/17.
6 | // Copyright 2017 Mayank Kumar. Available under the MIT license.
7 | //
8 |
9 | import Foundation
10 |
11 | /// Stores address-related keys for convenient access to an address dictionary (i.e. `AddressInfo` inside an `AddressAnalysisResult`)
12 | public struct Address {
13 |
14 | /// A key that corresponds to the city component of the address.
15 | public static let city = NSTextCheckingKey.city
16 |
17 | /// A key that corresponds to the zip code or postal code component of the address.
18 | public static let zip = NSTextCheckingKey.zip
19 |
20 | /// A key that corresponds to the street address component of the address.
21 | public static let street = NSTextCheckingKey.street
22 |
23 | /// A key that corresponds to the country component of the address.
24 | public static let country = NSTextCheckingKey.country
25 |
26 | /// A key that corresponds to the state or province component of the address.
27 | public static let state = NSTextCheckingKey.state
28 |
29 | // MARK:- Keys for which detection is unavailable in the original NSDataDetector API:
30 |
31 | // /// A key that corresponds to the name component of the address.
32 | // public static let name = NSTextCheckingNameKey
33 |
34 |
35 | // /// A key that corresponds to the phone number component of the address.
36 | // public static let phone = NSTextCheckingPhoneKey
37 |
38 | // /// A key that corresponds to the job component of the address.
39 | // public static let jobTitle = NSTextCheckingJobTitleKey
40 |
41 |
42 | // /// A key that corresponds to the organization component of the address.
43 | // public static let organization = NSTextCheckingOrganizationKey
44 | }
45 |
46 | /// Stores transit-related keys for convenient access to a transit dictionary (i.e. `TransitInfo` inside a `TransitAnalysisResult`
47 | public struct Transit {
48 |
49 | /// A key that corresponds to the flight component of a transit result.
50 | public static let flight = NSTextCheckingKey.flight
51 |
52 | //Keys for which detection is unavailable in the original NSDataDetector API:
53 |
54 | // /// A key that corresponds to the airline of a transit result.
55 | // public static let airline = NSTextCheckingAirlineKey
56 |
57 | }
58 |
--------------------------------------------------------------------------------
/MKDataDetectorTests/DateExtensionTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DateExtensionTests.swift
3 | // MKDataDetector
4 | //
5 | // Created by Jeet Parte on 22/07/17.
6 | // Copyright 2017 Mayank Kumar. Available under the MIT license.
7 | //
8 |
9 | import XCTest
10 | @testable import MKDataDetector
11 |
12 | class DateTests: XCTestCase {
13 |
14 | let dataDetectorService = MKDataDetectorService()
15 | var textBody = "I have a program at 7pm this Sunday, which ends on 11am Monday"
16 | var textBodies = ["Program", "Event at 9pm tomorrow", "11am party"]
17 | let type: NSTextCheckingResult.CheckingType = .date
18 | var nsDataDetector: NSDataDetector!
19 |
20 | override func setUp() {
21 | super.setUp()
22 | nsDataDetector = try! NSDataDetector(types: type.rawValue)
23 |
24 | //Change textbodies if required
25 | // textBody = ""
26 | // textBodies = []
27 | }
28 |
29 | func testSingleTextBody() {
30 | let results = dataDetectorService.extractDates(fromTextBody: textBody)
31 | let expectedCount = nsDataDetector.numberOfMatches(in: textBody, range: NSMakeRange(0, textBody.utf16.count))
32 | if expectedCount > 0 {
33 | XCTAssertNotNil(results)
34 | XCTAssertEqual(results!.count, expectedCount)
35 | } else {
36 | XCTAssertNil(results)
37 | }
38 | }
39 |
40 | func testMultipleTextBodies() {
41 | let combinedResults = dataDetectorService.extractDates(fromTextBodies: textBodies)
42 |
43 | //Check no.of matches for all textbodies
44 | let mergedTextBody = concatenate(textBodies)
45 | let totalMatches = nsDataDetector.numberOfMatches(in: mergedTextBody, range: NSMakeRange(0, mergedTextBody.utf16.count))
46 | if totalMatches > 0 {
47 | XCTAssertNotNil(combinedResults)
48 | for individualResult in combinedResults! {
49 | XCTAssertFalse(individualResult.isEmpty)
50 |
51 | //Check no. of matches for a present textbody
52 | let source = individualResult.first!.source //grab the textbody
53 | let expectedCount = nsDataDetector.numberOfMatches(in: source, range: NSMakeRange(0, source.utf16.count))
54 | XCTAssertEqual(individualResult.count, expectedCount)
55 | }
56 | } else {
57 | XCTAssertNil(combinedResults)
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/MKDataDetectorTests/PhoneNumberExtensionTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // PhoneNumberExtensionTests.swift
3 | // MKDataDetector
4 | //
5 | // Created by Jeet Parte on 23/07/17.
6 | // Copyright 2017 Mayank Kumar. Available under the MIT license.
7 | //
8 |
9 | import XCTest
10 | @testable import MKDataDetector
11 |
12 | class PhoneNumberTests: XCTestCase {
13 |
14 | let dataDetectorService = MKDataDetectorService()
15 | var textBody = "Mayank: +1 (413) 801 6324"
16 | var textBodies = ["+14138016324", "Mayank: +91 9920095040", "400053"]
17 | let type: NSTextCheckingResult.CheckingType = .phoneNumber
18 | var nsDataDetector: NSDataDetector!
19 |
20 | override func setUp() {
21 | super.setUp()
22 | nsDataDetector = try! NSDataDetector(types: type.rawValue)
23 |
24 | //Change textbodies if required
25 | // textBody = ""
26 | // textBodies = []
27 | }
28 |
29 | func testSingleTextBody() {
30 | let results = dataDetectorService.extractPhoneNumbers(fromTextBody: textBody)
31 | let expectedCount = nsDataDetector.numberOfMatches(in: textBody, range: NSMakeRange(0, textBody.utf16.count))
32 | if expectedCount > 0 {
33 | XCTAssertNotNil(results)
34 | XCTAssertEqual(results!.count, expectedCount)
35 | } else {
36 | XCTAssertNil(results)
37 | }
38 | }
39 |
40 | func testMultipleTextBodies() {
41 | let combinedResults = dataDetectorService.extractPhoneNumbers(fromTextBodies: textBodies)
42 |
43 | //Check no.of matches for all textbodies
44 | let mergedTextBody = concatenate(textBodies)
45 | let totalMatches = nsDataDetector.numberOfMatches(in: mergedTextBody, range: NSMakeRange(0, mergedTextBody.utf16.count))
46 | if totalMatches > 0 {
47 | XCTAssertNotNil(combinedResults)
48 | for individualResult in combinedResults! {
49 | XCTAssertFalse(individualResult.isEmpty)
50 |
51 | //Check no. of matches for a present textbody
52 | let source = individualResult.first!.source //grab the textbody
53 | let expectedCount = nsDataDetector.numberOfMatches(in: source, range: NSMakeRange(0, source.utf16.count))
54 | XCTAssertEqual(individualResult.count, expectedCount)
55 | }
56 | } else {
57 | XCTAssertNil(combinedResults)
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/MKDataDetector/UtilityExtensions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DataDetectorType.swift
3 | // MKDataDetector
4 | //
5 | // Created by Mayank Kumar on 7/10/17.
6 | // Copyright 2017 Mayank Kumar. Available under the MIT license.
7 | //
8 |
9 | import Foundation
10 |
11 | extension MKDataDetectorService {
12 |
13 | /// Supporting utility for generating an appropriate detector.
14 | ///
15 | /// - Parameter resultTypes: The types to detect.
16 | /// - Returns: An `NSDataDetector` or `nil` if it could not be instantiated.
17 | internal func dataDetectorOfType(withResultTypes resultTypes: [ResultType]) -> NSDataDetector? {
18 | do {
19 | var checkingTypes = NSTextCheckingResult.CheckingType()
20 | for resultType in resultTypes {
21 | guard let type = checkingTypeMap[resultType] else { return nil }
22 | checkingTypes.insert(type)
23 | }
24 | return checkingTypes.isEmpty ? nil : try NSDataDetector(types: checkingTypes.rawValue)
25 | } catch { return nil }
26 | }
27 |
28 |
29 | /// Supporting utility for retrieving results from matches with appropriate type.
30 | ///
31 | /// - Parameters:
32 | /// - match: An `NSTextCheckingResult` instance.
33 | /// - type: The type of match.
34 | /// - Returns: The result of the match casted generically.
35 | internal func retrieveData(fromMatch match: NSTextCheckingResult, withMatchType type: NSTextCheckingResult.CheckingType) -> T? {
36 | switch type {
37 | case NSTextCheckingResult.CheckingType.date:
38 | return match.date as? T
39 | case NSTextCheckingResult.CheckingType.address:
40 | return match.addressComponents as? T
41 | case NSTextCheckingResult.CheckingType.link:
42 | return match.url as? T
43 | case NSTextCheckingResult.CheckingType.phoneNumber:
44 | return match.phoneNumber as? T
45 | default:
46 | return match.components as? T
47 | }
48 | }
49 |
50 | /// Supporting utility for generating a substring from a string given a range.
51 | ///
52 | /// - Parameters:
53 | /// - textBody: A body of natural language text.
54 | /// - range: The range of the target substring.
55 | /// - Returns: A substring of `textBody` based on the `range`.
56 | internal func extractSource(fromTextBody textBody: String, usingRange range: NSRange) -> String {
57 | return (textBody as NSString).substring(with: range)
58 | }
59 |
60 | }
61 |
--------------------------------------------------------------------------------
/MKDataDetectorTests/LinkExtensionTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LinkExtensionTests.swift
3 | // MKDataDetector
4 | //
5 | // Created by Jeet Parte on 22/07/17.
6 | // Copyright 2017 Mayank Kumar. Available under the MIT license.
7 | //
8 |
9 | import XCTest
10 | @testable import MKDataDetector
11 |
12 | class LinkTests: XCTestCase {
13 |
14 | let dataDetectorService = MKDataDetectorService()
15 | var textBody = "My LinkedIn profile is https://www.linkedin.com/in/mayank-kumar-478245b1/, but my Github is at www.github.com/mayankk2308"
16 | var textBodies = ["My LinkedIn profile is https://www.linkedin.com/in/mayank-kumar-478245b1/", "My Github is at www.github.com/mayankk2308", "This has no links!"]
17 | let type: NSTextCheckingResult.CheckingType = .link
18 | var nsDataDetector: NSDataDetector!
19 |
20 | override func setUp() {
21 | super.setUp()
22 | nsDataDetector = try! NSDataDetector(types: type.rawValue)
23 |
24 | //Change textbodies if required
25 | // textBody = ""
26 | // textBodies = []
27 | }
28 |
29 | func testSingleTextBody() {
30 | let results = dataDetectorService.extractLinks(fromTextBody: textBody)
31 | let expectedCount = nsDataDetector.numberOfMatches(in: textBody, range: NSMakeRange(0, textBody.utf16.count))
32 | if expectedCount > 0 {
33 | XCTAssertNotNil(results)
34 | XCTAssertEqual(results!.count, expectedCount)
35 | } else {
36 | XCTAssertNil(results)
37 | }
38 | }
39 |
40 | func testMultipleTextBodies() {
41 | let combinedResults = dataDetectorService.extractLinks(fromTextBodies: textBodies)
42 |
43 | //Check no.of matches for all textbodies
44 | let mergedTextBody = concatenate(textBodies)
45 | let totalMatches = nsDataDetector.numberOfMatches(in: mergedTextBody, range: NSMakeRange(0, mergedTextBody.utf16.count))
46 | if totalMatches > 0 {
47 | XCTAssertNotNil(combinedResults)
48 | for individualResult in combinedResults! {
49 | XCTAssertFalse(individualResult.isEmpty)
50 |
51 | //Check no. of matches for a present textbody
52 | let source = individualResult.first!.source //grab the textbody
53 | let expectedCount = nsDataDetector.numberOfMatches(in: source, range: NSMakeRange(0, source.utf16.count))
54 | XCTAssertEqual(individualResult.count, expectedCount)
55 | }
56 | } else {
57 | XCTAssertNil(combinedResults)
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/MKDataDetectorTests/MultiDataTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MultiDataTests.swift
3 | // MKDataDetector
4 | //
5 | // Created by Jeet Parte on 23/07/17.
6 | // Copyright 2017 Mayank Kumar. Available under the MIT license.
7 | //
8 |
9 | import XCTest
10 | @testable import MKDataDetector
11 |
12 | class MultiDataTests: XCTestCase {
13 |
14 | let dataDetectorService = MKDataDetectorService()
15 | var mixedtextBody = "Call me on +1123456789 on 2017/13/12 at 5pm when you reach 990 Belchertown Rd, Amherst, MA - 01440 USA. Checkout out my website www.example.com. I'm flying to Paris tomorrow by Flight 2223"
16 | var mixedTextBodies = ["Call me on +1123456789", "on 2017/13/12 at 5pm", "when you reach 990 Belchertown Rd, Amherst, MA - 01440 USA", "Checkout out my website www.example.com", "I'm flying to Paris tomorrow by Flight 2223"]
17 | var types: NSTextCheckingResult.CheckingType!
18 | var nsDataDetector: NSDataDetector!
19 |
20 | override func setUp() {
21 | super.setUp()
22 | types = [.date, .address, .link, .phoneNumber, .transitInformation]
23 | nsDataDetector = try! NSDataDetector(types: types.rawValue)
24 |
25 | //Change textbodies here
26 | // mixedtextBody = ""
27 | // mixedTextBodies = []
28 | }
29 | func testSingleTextBody() {
30 | let results = dataDetectorService.extractInformation(fromTextBody: mixedtextBody, withResultTypes: .date, .address, .link, .phoneNumber, .transitInformation)
31 | let expectedCount = nsDataDetector.numberOfMatches(in: mixedtextBody, range: NSMakeRange(0, mixedtextBody.utf16.count))
32 | if expectedCount > 0 {
33 | XCTAssertNotNil(results)
34 | XCTAssertEqual(results!.count, expectedCount)
35 | } else {
36 | XCTAssertNil(results)
37 | }
38 | }
39 |
40 | func testMultipleTextBodies() {
41 | let combinedResults = dataDetectorService.extractInformation(fromTextBodies: mixedTextBodies, withResultTypes: .date, .address, .link, .phoneNumber, .transitInformation)
42 |
43 | //Check no.of matches for all textbodies
44 | let mergedTextBody = concatenate(mixedTextBodies)
45 | let totalMatches = nsDataDetector.numberOfMatches(in: mergedTextBody, range: NSMakeRange(0, mergedTextBody.utf16.count))
46 | if totalMatches > 0 {
47 | XCTAssertNotNil(combinedResults)
48 | for individualResult in combinedResults! {
49 | XCTAssertFalse(individualResult.isEmpty)
50 |
51 | //Check no. of matches for a present textbody
52 | let source = individualResult.first!.source //grab the textbody
53 | let expectedCount = nsDataDetector.numberOfMatches(in: source, range: NSMakeRange(0, source.utf16.count))
54 | XCTAssertEqual(individualResult.count, expectedCount)
55 | }
56 | } else {
57 | XCTAssertNil(combinedResults)
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/MKDataDetector.xcodeproj/xcshareddata/xcschemes/MKDataDetector.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
43 |
44 |
50 |
51 |
52 |
53 |
59 |
60 |
66 |
67 |
68 |
69 |
71 |
72 |
75 |
76 |
77 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
6 |
7 | ## Our Standards
8 |
9 | Examples of behavior that contributes to creating a positive environment include:
10 |
11 | * Using welcoming and inclusive language
12 | * Being respectful of differing viewpoints and experiences
13 | * Gracefully accepting constructive criticism
14 | * Focusing on what is best for the community
15 | * Showing empathy towards other community members
16 |
17 | Examples of unacceptable behavior by participants include:
18 |
19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances
20 | * Trolling, insulting/derogatory comments, and personal or political attacks
21 | * Public or private harassment
22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission
23 | * Other conduct which could reasonably be considered inappropriate in a professional setting
24 |
25 | ## Our Responsibilities
26 |
27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
28 |
29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
30 |
31 | ## Scope
32 |
33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
34 |
35 | ## Enforcement
36 |
37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at mayankk2308@gmail.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
38 |
39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
40 |
41 | ## Attribution
42 |
43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
44 |
45 | [homepage]: http://contributor-covenant.org
46 | [version]: http://contributor-covenant.org/version/1/4/
47 |
--------------------------------------------------------------------------------
/MKDataDetector/Address.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Address.swift
3 | // MKDataDetector
4 | //
5 | // Created by Mayank Kumar on 7/9/17.
6 | // Copyright 2017 Mayank Kumar. Available under the MIT license.
7 | //
8 |
9 | import Foundation
10 | import CoreLocation
11 |
12 | extension MKDataDetectorService {
13 |
14 | /// Retrieves address components matched from a given body of text along with the range of each substring that matched.
15 | ///
16 | /// - Parameter textBody: A body of natural language text.
17 | /// - Returns: An array of `AddressAnalysisResult` instances or `nil` if no address components could be found.
18 | public func extractAddresses(fromTextBody textBody: String) -> [AddressAnalysisResult]? {
19 | return extractData(fromTextBody: textBody, withResultTypes: [.address])
20 | }
21 |
22 | /// Retrieves address components matched from the given bodies of text along with the range of each substring that matched.
23 | ///
24 | /// - Parameter textBodies: A set of multiple bodies of natural language text.
25 | /// - Returns: An array of `[AddressAnalysisResult]` instances or `nil` if no address components could be found.
26 | /// - Note: Inside the returned array, a non-empty `[AddressAnalysisResult]` instance is returned for each text body which contains a match.
27 | public func extractAddresses(fromTextBodies textBodies: [String]) -> [[AddressAnalysisResult]]? {
28 | return extractData(fromTextBodies: textBodies, withResultTypes: [.address])
29 | }
30 |
31 | /// Retrieves the geographic location of an address analysis result.
32 | ///
33 | /// - Parameters:
34 | /// - result: The address analysis result.
35 | /// - completion: Called with a `CLLocation` object, which will be `nil` if it could not be generated.
36 | public func extractLocation(fromAnalysisResult result: AddressAnalysisResult, onCompletion completion: @escaping locationCompletion) {
37 | extractLocation(fromAddress: result.source, onCompletion: completion)
38 | }
39 |
40 | /// Retrieves the geographic location of a valid address.
41 | ///
42 | /// - Parameters:
43 | /// - address: A valid address.
44 | /// - completion: Called with a `CLLocation` object, which will be `nil` if it could not be generated.
45 | public func extractLocation(fromAddress address: String, onCompletion completion: @escaping locationCompletion) {
46 | let geocoder = CLGeocoder()
47 | geocoder.geocodeAddressString(address) { placemarks, error in
48 | guard let location = self.extractLocation(fromPlacemarks: placemarks, withError: error) else { return }
49 | completion(location)
50 | }
51 | }
52 |
53 | /// Retrieves the geographic location obtained from the first placemark.
54 | ///
55 | /// - Parameters:
56 | /// - placemarks: An array of placemarks.
57 | /// - error: An error that may have occurred in retrieval.
58 | /// - Returns: A `CLLocation` object or `nil` if it could not be generated.
59 | internal func extractLocation(fromPlacemarks placemarks: [CLPlacemark]?, withError error: Error?) -> CLLocation? {
60 | if error == nil {
61 | guard let placemarks = placemarks, let placemark = placemarks.first else { return nil }
62 | return placemark.location
63 | }
64 | return nil
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/MKDataDetectorTests/StringAccessTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // StringAccessTests.swift
3 | // MKDataDetector
4 | //
5 | // Created by Jeet Parte on 23/07/17.
6 | // Copyright 2017 Mayank Kumar. Available under the MIT license.
7 | //
8 |
9 | import XCTest
10 | @testable import MKDataDetector
11 |
12 | class StringAccessTests: XCTestCase {
13 |
14 | var testString = "This test was written at 1:34 PM"
15 | var type: NSTextCheckingResult.CheckingType!
16 | var nsDataDetector: NSDataDetector!
17 |
18 | override func setUp() {
19 | super.setUp()
20 |
21 | //Change test string here
22 | // testString = "+14138016324"
23 | // testString = ""
24 | }
25 |
26 | func testDates() {
27 | let dates = testString.dates
28 | type = .date
29 | nsDataDetector = try! NSDataDetector(types: type.rawValue)
30 | let expectedCount = nsDataDetector.numberOfMatches(in: testString, range: NSMakeRange(0, testString.utf16.count))
31 | if expectedCount > 0 {
32 | XCTAssertNotNil(dates)
33 | XCTAssertEqual(dates!.count, expectedCount)
34 | } else {
35 | XCTAssertNil(dates)
36 | }
37 | }
38 |
39 | func testLinks() {
40 | let links = testString.links
41 | type = .link
42 | nsDataDetector = try! NSDataDetector(types: type.rawValue)
43 | let expectedCount = nsDataDetector.numberOfMatches(in: testString, range: NSMakeRange(0, testString.utf16.count))
44 | if expectedCount > 0 {
45 | XCTAssertNotNil(links)
46 | XCTAssertEqual(links!.count, expectedCount)
47 | } else {
48 | XCTAssertNil(links)
49 | }
50 | }
51 |
52 | func testAddresses() {
53 | let addresses = testString.addresses
54 | type = .address
55 | nsDataDetector = try! NSDataDetector(types: type.rawValue)
56 | let expectedCount = nsDataDetector.numberOfMatches(in: testString, range: NSMakeRange(0, testString.utf16.count))
57 | if expectedCount > 0 {
58 | XCTAssertNotNil(addresses)
59 | XCTAssertEqual(addresses!.count, expectedCount)
60 | } else {
61 | XCTAssertNil(addresses)
62 | }
63 | }
64 |
65 | func testPhoneNumbers() {
66 | let phoneNumbers = testString.phoneNumbers
67 | type = .phoneNumber
68 | nsDataDetector = try! NSDataDetector(types: type.rawValue)
69 | let expectedCount = nsDataDetector.numberOfMatches(in: testString, range: NSMakeRange(0, testString.utf16.count))
70 | if expectedCount > 0 {
71 | XCTAssertNotNil(phoneNumbers)
72 | XCTAssertEqual(phoneNumbers!.count, expectedCount)
73 | } else {
74 | XCTAssertNil(phoneNumbers)
75 | }
76 | }
77 |
78 | func testTransitInfo() {
79 | let transitInfo = testString.transitInfo
80 | type = .transitInformation
81 | nsDataDetector = try! NSDataDetector(types: type.rawValue)
82 | let expectedCount = nsDataDetector.numberOfMatches(in: testString, range: NSMakeRange(0, testString.utf16.count))
83 | if expectedCount > 0 {
84 | XCTAssertNotNil(transitInfo)
85 | XCTAssertEqual(transitInfo!.count, expectedCount)
86 | } else {
87 | XCTAssertNil(transitInfo)
88 | }
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/MKDataDetector/StringAccess.swift:
--------------------------------------------------------------------------------
1 | //
2 | // StringAccess.swift
3 | // MKDataDetector
4 | //
5 | // Created by Mayank Kumar on 7/16/17.
6 | // Copyright 2017 Mayank Kumar. Available under the MIT license.
7 | //
8 |
9 | import Foundation
10 | #if os(OSX)
11 | import Cocoa
12 | #else
13 | import UIKit
14 | #endif
15 |
16 | public extension String {
17 |
18 | /// Retrieves an array of matched dates in the string or `nil` if no matches could be found.
19 | var dates: [Date]? {
20 | return retrieveMappedData(withResultType: .date)
21 | }
22 |
23 | /// Retrieves an array of matched links in the string or `nil` if no matches could be found.
24 | var links: [URL]? {
25 | return retrieveMappedData(withResultType: .link)
26 | }
27 |
28 | /// Retrieves an array of matched addresses in the string or `nil` if no matches could be found.
29 | var addresses: [AddressInfo]? {
30 | return retrieveMappedData(withResultType: .address)
31 | }
32 |
33 | /// Retrieves an array of matched phone numbers in the string or `nil` if no matches could be found.
34 | var phoneNumbers: [String]? {
35 | return retrieveMappedData(withResultType: .phoneNumber)
36 | }
37 |
38 | /// Retrieves an array of matched transit information in the string or `nil` if no matches could be found.
39 | var transitInfo: [TransitInfo]? {
40 | return retrieveMappedData(withResultType: .transitInformation)
41 | }
42 |
43 | /// Retrieves generic data for a requested result type.
44 | ///
45 | /// - Parameter type: The type to match.
46 | /// - Returns: An array of generic results or `nil` if no matches could be found.
47 | private func retrieveMappedData(withResultType type: ResultType) -> [T]? {
48 | let dataDetectorService = MKDataDetectorService()
49 | guard let results: [AnalysisResult] = dataDetectorService.extractData(fromTextBody: self, withResultTypes: [type]) else { return nil }
50 | return results.compactMap { $0.data }
51 | }
52 |
53 | }
54 |
55 | internal extension String {
56 |
57 | /// The string with additional whitespaces condensed to a single whitespace.
58 | var condensedWhitespace: String {
59 | let components = self.components(separatedBy: .whitespacesAndNewlines)
60 | return components.filter { !$0.isEmpty }.joined(separator: " ")
61 | }
62 |
63 | }
64 |
65 | extension MKDataDetectorService {
66 |
67 | /// Generates attributed text for the user interface given a set of analysis results on a text body.
68 | ///
69 | /// - Parameters:
70 | /// - results: An array of analysis results.
71 | /// - color: A color for highlighting matches.
72 | /// - Returns: An attributed string on the basis of the matches.
73 | public func attributedText(fromAnalysisResults results: [AnalysisResult], withColor color: CGColor) -> NSMutableAttributedString? {
74 | guard let firstResult = results.first else { return nil }
75 | let attributedString = NSMutableAttributedString(string: firstResult.source)
76 | for result in results {
77 | #if os(iOS)
78 | attributedString.addAttribute(NSAttributedString.Key.foregroundColor, value: UIColor(cgColor: color), range: result.rangeInSource)
79 | #elseif os(OSX)
80 | attributedString.addAttribute(NSAttributedString.Key.foregroundColor, value: NSColor(cgColor: color) as Any, range: result.rangeInSource)
81 | #endif
82 | }
83 | return attributedString
84 | }
85 |
86 | }
87 |
--------------------------------------------------------------------------------
/MKDataDetector/Date.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Date.swift
3 | // MKDataDetector
4 | //
5 | // Created by Mayank Kumar on 7/9/17.
6 | // Copyright 2017 Mayank Kumar. Available under the MIT license.
7 | //
8 |
9 | #if !os(tvOS)
10 | import EventKit
11 | #endif
12 | import Foundation
13 |
14 | extension MKDataDetectorService {
15 |
16 | /// Retrieves dates matched from a given body of text along with the range of each substring that matched.
17 | ///
18 | /// - Parameter textBody: A body of natural language text.
19 | /// - Returns: An array of `DateAnalysisResult` instances or `nil` if no dates could be found.
20 | public func extractDates(fromTextBody textBody: String) -> [DateAnalysisResult]? {
21 | return extractData(fromTextBody: textBody, withResultTypes: [.date])
22 | }
23 |
24 | /// Retrieves dates matched from the given bodies of text along with the range of each substring that matched.
25 | ///
26 | /// - Parameter textBodies: A set of multiple bodies of natural language text.
27 | /// - Returns: An array of `[DateAnalysisResult]` instances or `nil` if no dates could be found.
28 | /// - Note: Inside the returned array, a non-empty `[DateAnalysisResult]` instance is returned for each text body which contains a match.
29 | public func extractDates(fromTextBodies textBodies: [String]) -> [[DateAnalysisResult]]? {
30 | return extractData(fromTextBodies: textBodies, withResultTypes: [.date])
31 | }
32 |
33 | #if !os(tvOS)
34 | /// Generates an `EKEvent` for a given analysis result and event store.
35 | ///
36 | /// - Parameters:
37 | /// - store: The event store for which to generate the event.
38 | /// - result: The `DateAnalysisResult` previously procured from analysis.
39 | /// - endDate: The user-specified end date - defaults to an hour from start date otherwise.
40 | /// - Returns: The generated event.
41 | ///
42 | /// - Note: This function auto-generates the event name based on date analysis. This feature requires additional testing and may not always yield satisfactory results. The default generator is exposed for use.
43 | public func generateEvent(forEventStore store: EKEventStore, withAnalysisResult result: DateAnalysisResult, withEndDate endDate: Date? = nil) -> EKEvent {
44 | let eventName = (result.source as NSString).replacingCharacters(in: result.rangeInSource, with: "").condensedWhitespace.trimmingCharacters(in: .whitespaces)
45 | let extractedDate: Date = endDate != nil ? endDate! : result.data.addingTimeInterval(3600)
46 | return generateEvent(forEventStore: store, withEventName: eventName, withStartDate: result.data, withEndDate: extractedDate)
47 | }
48 |
49 |
50 | /// Generates an `EKEvent` for a given set of precise data along with an event store.
51 | ///
52 | /// - Parameters:
53 | /// - store: The event store for which to generate the event.
54 | /// - name: The name of the event.
55 | /// - startDate: The start date/time of the event.
56 | /// - endDate: The end date/time of the event.
57 | /// - Returns: The generated event.
58 | public func generateEvent(forEventStore store: EKEventStore, withEventName name: String, withStartDate startDate: Date, withEndDate endDate: Date) -> EKEvent {
59 | let event = EKEvent(eventStore: store)
60 | event.title = name
61 | event.startDate = startDate
62 | event.endDate = endDate
63 | return event
64 | }
65 | #endif
66 |
67 | }
68 |
--------------------------------------------------------------------------------
/MKDataDetector.xcodeproj/xcshareddata/xcschemes/MKDataDetectorTests.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
37 |
38 |
39 |
40 |
42 |
48 |
49 |
50 |
51 |
52 |
62 |
63 |
69 |
70 |
71 |
72 |
78 |
79 |
81 |
82 |
85 |
86 |
87 |
--------------------------------------------------------------------------------
/MKDataDetector/MKDataDetectorService.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MKDataDetectorService.swift
3 | // MKDataDetector
4 | //
5 | // Created by Mayank Kumar on 7/8/17.
6 | // Copyright 2017 Mayank Kumar. Available under the MIT license.
7 | //
8 |
9 | import Foundation
10 |
11 | /// The primary service class that drives MKDataDetector capabilities.
12 | public class MKDataDetectorService {
13 |
14 | /// Maps `ResultType` types to `NSTextCheckingResult.CheckingType` types.
15 | internal let checkingTypeMap = [
16 | ResultType.date: NSTextCheckingResult.CheckingType.date,
17 | ResultType.address: NSTextCheckingResult.CheckingType.address,
18 | ResultType.link: NSTextCheckingResult.CheckingType.link,
19 | ResultType.phoneNumber: NSTextCheckingResult.CheckingType.phoneNumber,
20 | ResultType.transitInformation: NSTextCheckingResult.CheckingType.transitInformation
21 | ]
22 |
23 | /// Maps `NSTextCheckingResult.CheckingType.RawValue` types to `ResultType` types.
24 | internal let inverseCheckingTypeMap: [NSTextCheckingResult.CheckingType.RawValue : ResultType] = [
25 | NSTextCheckingResult.CheckingType.date.rawValue: ResultType.date,
26 | NSTextCheckingResult.CheckingType.address.rawValue: ResultType.address,
27 | NSTextCheckingResult.CheckingType.link.rawValue: ResultType.link,
28 | NSTextCheckingResult.CheckingType.phoneNumber.rawValue: ResultType.phoneNumber,
29 | NSTextCheckingResult.CheckingType.transitInformation.rawValue: ResultType.transitInformation
30 | ]
31 |
32 | /// Initializes and returns an `MKDataDetectorService` instance.
33 | public init() {}
34 |
35 | /// Generic implementation for extracting data from a single body of text.
36 | ///
37 | /// - Parameters:
38 | /// - textBody: A body of natural language text.
39 | /// - detector: The detector to use for extraction.
40 | /// - types: Types to match.
41 | /// - Returns: A generic array of `AnalysisResult` instances or `nil` if no matches could be found.
42 | /// - Note: The `detector` parameter is optional, and is only useful for preventing redundant generation of the data detector in subsequent requests to this function.
43 | internal func extractData(fromTextBody textBody: String, withDetector detector: NSDataDetector? = nil, withResultTypes types: [ResultType]) -> [AnalysisResult]? {
44 | var analysisResults = [AnalysisResult]()
45 | let dataDetector: NSDataDetector
46 | if detector != nil {
47 | dataDetector = detector!
48 | } else {
49 | guard let newDetector = dataDetectorOfType(withResultTypes: types) else { return nil }
50 | dataDetector = newDetector
51 | }
52 | let matches = dataDetector.matches(in: textBody, range: NSRange(location: 0, length: (textBody as NSString).length))
53 | if matches.isEmpty {
54 | return nil
55 | } else {
56 | for match in matches {
57 | let range = match.range
58 | let source = textBody
59 | let dataString = extractSource(fromTextBody: textBody, usingRange: range)
60 | guard let data: T = retrieveData(fromMatch: match, withMatchType: match.resultType) else { continue }
61 | guard let resultType = inverseCheckingTypeMap[match.resultType.rawValue] else { continue }
62 | let analysisResult = AnalysisResult(source: source, rangeInSource: range, dataString: dataString, dataType: resultType, data: data)
63 | analysisResults.append(analysisResult)
64 | }
65 | }
66 | return analysisResults.isEmpty ? nil : analysisResults
67 | }
68 |
69 | /// Generic implementation for extracting data from a multiple bodies of text.
70 | ///
71 | /// - Parameters:
72 | /// - textBodies: An array of multiple bodies of natural language text.
73 | /// - types: The types to match.
74 | /// - Returns: A generic array of `[AnalysisResult]` instances or `nil` if no matches could be found in any of the text bodies.
75 | /// - Note: Inside the returned array, a non-empty `[AnalysisResult]` instance is returned for each text body which contains a match.
76 | internal func extractData(fromTextBodies textBodies: [String], withResultTypes types: [ResultType]) -> [[AnalysisResult]]? {
77 | var result = [[AnalysisResult]]()
78 | guard let detector = dataDetectorOfType(withResultTypes: types) else { return nil }
79 | for textBody in textBodies {
80 | guard let extractedData: [AnalysisResult] = extractData(fromTextBody: textBody, withDetector: detector, withResultTypes: types) else { continue }
81 | result.append(extractedData)
82 | }
83 | return result.isEmpty ? nil : result
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/TestGround-iOS.playground/Contents.swift:
--------------------------------------------------------------------------------
1 | // Test below - delete tests before commits, or exclude this file in your commits
2 | import MKDataDetector
3 | import CoreLocation
4 | import UIKit
5 |
6 | let dataDetectorService = MKDataDetectorService()
7 |
8 | let textBody = "hello world at 9pm tomorrow"
9 | let textBodies = ["event on monday", "movie on wednesday", "payment due next thursday", "time", "money", "yellowstone park visit next friday", "party next friday at 8pm"]
10 |
11 | let address = "5123 Kere street, get here fast MA 01003, USA"
12 | let legalAddress = "133 Belchertown Rd Amherst MA - 01003 United States"
13 | let addresses = ["B713, Samartha Aangan II, Oshiwara, Andheri West, Mumbai - 400053, India", "133 Belchertown Rd Amherst MA - 01003 United States"]
14 |
15 | let link = "Mayank's Github: https://github.com/mayankk2308"
16 | let links = [link, "another link ://ssdsd", "testlink://", "www.apple.com", "http://apple.com"]
17 |
18 | let phone = "Mayank: +1 (413) 801 6324"
19 | let phones = ["+14138016324", "Mayank: +91 9920095040", "400053"]
20 |
21 | let transit = "UA 2392"
22 | let transits = ["Flight 2334", "EK 239 to Boston", "Emirates Airlines", "United Flight 2223"]
23 |
24 | print("-----Single Body Date Results-----\n")
25 | if let singleBodyDateResults = dataDetectorService.extractDates(fromTextBody: textBody) {
26 | for result in singleBodyDateResults {
27 | print(result.source)
28 | print(result.data)
29 | print()
30 | }
31 | }
32 |
33 | print("\n-----Multiple Bodies Date Results-----\n")
34 |
35 | if let combinedDateResults = dataDetectorService.extractDates(fromTextBodies: textBodies) {
36 | for individualResults in combinedDateResults {
37 | for result in individualResults {
38 | print(result.source)
39 | print(result.data)
40 | print()
41 | }
42 | }
43 | }
44 |
45 | print("\n-----Single Body Address Results-----\n")
46 |
47 | if let addressResults = dataDetectorService.extractAddresses(fromTextBody: address) {
48 | for result in addressResults {
49 | print(result.source)
50 | print(result.data)
51 | print()
52 | }
53 | }
54 |
55 | print("\n-----Multiple Bodies Address Results-----\n")
56 |
57 | if let combinedAddressResults = dataDetectorService.extractAddresses(fromTextBodies: addresses) {
58 | for individualResults in combinedAddressResults {
59 | for result in individualResults {
60 | print(result.source)
61 | print(result.data)
62 | print()
63 | }
64 | }
65 | }
66 |
67 | print("\n-----Single Body URL Results-----\n")
68 |
69 | if let URLResults = dataDetectorService.extractLinks(fromTextBody: link) {
70 | for result in URLResults {
71 | print(result.source)
72 | print(result.data)
73 | print()
74 | }
75 | }
76 |
77 | print("\n-----Multiple Bodies URL Results-----\n")
78 |
79 | if let combinedURLResults = dataDetectorService.extractLinks(fromTextBodies: links) {
80 | for individualResults in combinedURLResults {
81 | for result in individualResults {
82 | print(result.source)
83 | print(result.data)
84 | print()
85 | }
86 | }
87 | }
88 |
89 | print("\n-----Single Body Phone Results-----\n")
90 |
91 | if let phoneResults = dataDetectorService.extractPhoneNumbers(fromTextBody: phone) {
92 | for result in phoneResults {
93 | print(result.source)
94 | print(result.data)
95 | print()
96 | }
97 | }
98 |
99 | print("\n-----Multiple Bodies Phone Results-----\n")
100 |
101 | if let combinedPhoneResults = dataDetectorService.extractPhoneNumbers(fromTextBodies: phones) {
102 | for individualResults in combinedPhoneResults {
103 | for result in individualResults {
104 | print(result.source)
105 | print(result.data)
106 | print()
107 | }
108 | }
109 | }
110 |
111 | print("\n-----Single Body Transit Results-----\n")
112 |
113 | if let transitResults = dataDetectorService.extractTransitInformation(fromTextBody: transit) {
114 | for result in transitResults {
115 | print(result.source)
116 | print(result.data)
117 | print()
118 | }
119 | }
120 |
121 | print("\n-----Multiple Bodies Transit Results-----\n")
122 |
123 | if let combinedTransitResults = dataDetectorService.extractTransitInformation(fromTextBodies: transits) {
124 | for individualResults in combinedTransitResults {
125 | for result in individualResults {
126 | print(result.source)
127 | print(result.data)
128 | print()
129 | }
130 | }
131 | }
132 |
133 | print("\n-----Basic Scenario Implementation Results-----\n")
134 |
135 | print(textBody.dates!)
136 | print(link.links!)
137 | print(transit.transitInfo!)
138 | print(address.addresses!)
139 | print(phone.phoneNumbers!)
140 |
141 | print("\n-----Mixed Request Results-----\n")
142 |
143 | let mixedText = "Call Mayank on +1123456789 on 2017/13/12 at 5pm when you reach 990 Belchertown Rd, Amherst, MA - 01440 USA."
144 |
145 | if let results = dataDetectorService.extractInformation(fromTextBody: mixedText, withResultTypes: .date, .address, .phoneNumber) {
146 | for result in results {
147 | switch result.dataType {
148 | case .date:
149 | let date = result.data as! Date
150 | print(date)
151 | break
152 | case .address:
153 | let address = result.data as! AddressInfo
154 | print(address)
155 | break
156 | case .phoneNumber:
157 | let phoneNumber = result.data as! String
158 | print(phoneNumber)
159 | break
160 | default:
161 | continue
162 | }
163 | }
164 | }
165 |
--------------------------------------------------------------------------------
/TestGround-macOS.playground/Contents.swift:
--------------------------------------------------------------------------------
1 | // Test below - delete tests before commits, or exclude this file in your commits
2 | import MKDataDetector
3 | import CoreLocation
4 | import Cocoa
5 |
6 | let dataDetectorService = MKDataDetectorService()
7 |
8 | let textBody = "hello world at 9pm tomorrow"
9 | let textBodies = ["event on monday", "movie on wednesday", "payment due next thursday", "time", "money", "yellowstone park visit next friday", "party next friday at 8pm"]
10 |
11 | let address = "5123 Kere street, get here fast MA 01003, USA"
12 | let legalAddress = "133 Belchertown Rd Amherst MA - 01003 United States"
13 | let addresses = ["B713, Samartha Aangan II, Oshiwara, Andheri West, Mumbai - 400053, India", "133 Belchertown Rd Amherst MA - 01003 United States"]
14 |
15 | let link = "Mayank's Github: https://github.com/mayankk2308"
16 | let links = [link, "another link ://ssdsd", "testlink://", "www.apple.com", "http://apple.com"]
17 |
18 | let phone = "Mayank: +1 (413) 801 6324"
19 | let phones = ["+14138016324", "Mayank: +91 9920095040", "400053"]
20 |
21 | let transit = "UA 2392"
22 | let transits = ["Flight 2334", "EK 239 to Boston", "Emirates Airlines", "United Flight 2223"]
23 |
24 | print("-----Single Body Date Results-----\n")
25 | if let singleBodyDateResults = dataDetectorService.extractDates(fromTextBody: textBody) {
26 | for result in singleBodyDateResults {
27 | print(result.source)
28 | print(result.data)
29 | print()
30 | }
31 | }
32 |
33 | print("\n-----Multiple Bodies Date Results-----\n")
34 |
35 | if let combinedDateResults = dataDetectorService.extractDates(fromTextBodies: textBodies) {
36 | for individualResults in combinedDateResults {
37 | for result in individualResults {
38 | print(result.source)
39 | print(result.data)
40 | print()
41 | }
42 | }
43 | }
44 |
45 | print("\n-----Single Body Address Results-----\n")
46 |
47 | if let addressResults = dataDetectorService.extractAddresses(fromTextBody: address) {
48 | for result in addressResults {
49 | print(result.source)
50 | print(result.data)
51 | print()
52 | }
53 | }
54 |
55 | print("\n-----Multiple Bodies Address Results-----\n")
56 |
57 | if let combinedAddressResults = dataDetectorService.extractAddresses(fromTextBodies: addresses) {
58 | for individualResults in combinedAddressResults {
59 | for result in individualResults {
60 | print(result.source)
61 | print(result.data)
62 | print()
63 | }
64 | }
65 | }
66 |
67 | print("\n-----Single Body URL Results-----\n")
68 |
69 | if let URLResults = dataDetectorService.extractLinks(fromTextBody: link) {
70 | for result in URLResults {
71 | print(result.source)
72 | print(result.data)
73 | print()
74 | }
75 | }
76 |
77 | print("\n-----Multiple Bodies URL Results-----\n")
78 |
79 | if let combinedURLResults = dataDetectorService.extractLinks(fromTextBodies: links) {
80 | for individualResults in combinedURLResults {
81 | for result in individualResults {
82 | print(result.source)
83 | print(result.data)
84 | print()
85 | }
86 | }
87 | }
88 |
89 | print("\n-----Single Body Phone Results-----\n")
90 |
91 | if let phoneResults = dataDetectorService.extractPhoneNumbers(fromTextBody: phone) {
92 | for result in phoneResults {
93 | print(result.source)
94 | print(result.data)
95 | print()
96 | }
97 | }
98 |
99 | print("\n-----Multiple Bodies Phone Results-----\n")
100 |
101 | if let combinedPhoneResults = dataDetectorService.extractPhoneNumbers(fromTextBodies: phones) {
102 | for individualResults in combinedPhoneResults {
103 | for result in individualResults {
104 | print(result.source)
105 | print(result.data)
106 | print()
107 | }
108 | }
109 | }
110 |
111 | print("\n-----Single Body Transit Results-----\n")
112 |
113 | if let transitResults = dataDetectorService.extractTransitInformation(fromTextBody: transit) {
114 | for result in transitResults {
115 | print(result.source)
116 | print(result.data)
117 | print()
118 | }
119 | }
120 |
121 | print("\n-----Multiple Bodies Transit Results-----\n")
122 |
123 | if let combinedTransitResults = dataDetectorService.extractTransitInformation(fromTextBodies: transits) {
124 | for individualResults in combinedTransitResults {
125 | for result in individualResults {
126 | print(result.source)
127 | print(result.data)
128 | print()
129 | }
130 | }
131 | }
132 |
133 | print("\n-----Basic Scenario Implementation Results-----\n")
134 |
135 | print(textBody.dates!)
136 | print(link.links!)
137 | print(transit.transitInfo!)
138 | print(address.addresses!)
139 | print(phone.phoneNumbers!)
140 |
141 | print("\n-----Mixed Request Results-----\n")
142 |
143 | let mixedText = "Call Mayank on +1123456789 on 2017/13/12 at 5pm when you reach 990 Belchertown Rd, Amherst, MA - 01440 USA."
144 |
145 | if let results = dataDetectorService.extractInformation(fromTextBody: mixedText, withResultTypes: .date, .address, .phoneNumber) {
146 | for result in results {
147 | switch result.dataType {
148 | case .date:
149 | let date = result.data as! Date
150 | print(date)
151 | break
152 | case .address:
153 | let address = result.data as! AddressInfo
154 | print(address)
155 | break
156 | case .phoneNumber:
157 | let phoneNumber = result.data as! String
158 | print(phoneNumber)
159 | break
160 | default:
161 | continue
162 | }
163 | }
164 | }
165 |
--------------------------------------------------------------------------------
/TestGround-tvOS.playground/Contents.swift:
--------------------------------------------------------------------------------
1 | // Test below - delete tests before commits, or exclude this file in your commits
2 | import MKDataDetector
3 | import CoreLocation
4 | import UIKit
5 |
6 | let dataDetectorService = MKDataDetectorService()
7 |
8 | let textBody = "hello world at 9pm tomorrow"
9 | let textBodies = ["event on monday", "movie on wednesday", "payment due next thursday", "time", "money", "yellowstone park visit next friday", "party next friday at 8pm"]
10 |
11 | let address = "5123 Kere street, get here fast MA 01003, USA"
12 | let legalAddress = "133 Belchertown Rd Amherst MA - 01003 United States"
13 | let addresses = ["B713, Samartha Aangan II, Oshiwara, Andheri West, Mumbai - 400053, India", "133 Belchertown Rd Amherst MA - 01003 United States"]
14 |
15 | let link = "Mayank's Github: https://github.com/mayankk2308"
16 | let links = [link, "another link ://ssdsd", "testlink://", "www.apple.com", "http://apple.com"]
17 |
18 | let phone = "Mayank: +1 (413) 801 6324"
19 | let phones = ["+14138016324", "Mayank: +91 9920095040", "400053"]
20 |
21 | let transit = "UA 2392"
22 | let transits = ["Flight 2334", "EK 239 to Boston", "Emirates Airlines", "United Flight 2223"]
23 |
24 | print("-----Single Body Date Results-----\n")
25 | if let singleBodyDateResults = dataDetectorService.extractDates(fromTextBody: textBody) {
26 | for result in singleBodyDateResults {
27 | print(result.source)
28 | print(result.data)
29 | print()
30 | }
31 | }
32 |
33 | print("\n-----Multiple Bodies Date Results-----\n")
34 |
35 | if let combinedDateResults = dataDetectorService.extractDates(fromTextBodies: textBodies) {
36 | for individualResults in combinedDateResults {
37 | for result in individualResults {
38 | print(result.source)
39 | print(result.data)
40 | print()
41 | }
42 | }
43 | }
44 |
45 | print("\n-----Single Body Address Results-----\n")
46 |
47 | if let addressResults = dataDetectorService.extractAddresses(fromTextBody: address) {
48 | for result in addressResults {
49 | print(result.source)
50 | print(result.data)
51 | print()
52 | }
53 | }
54 |
55 | print("\n-----Multiple Bodies Address Results-----\n")
56 |
57 | if let combinedAddressResults = dataDetectorService.extractAddresses(fromTextBodies: addresses) {
58 | for individualResults in combinedAddressResults {
59 | for result in individualResults {
60 | print(result.source)
61 | print(result.data)
62 | print()
63 | }
64 | }
65 | }
66 |
67 | print("\n-----Single Body URL Results-----\n")
68 |
69 | if let URLResults = dataDetectorService.extractLinks(fromTextBody: link) {
70 | for result in URLResults {
71 | print(result.source)
72 | print(result.data)
73 | print()
74 | }
75 | }
76 |
77 | print("\n-----Multiple Bodies URL Results-----\n")
78 |
79 | if let combinedURLResults = dataDetectorService.extractLinks(fromTextBodies: links) {
80 | for individualResults in combinedURLResults {
81 | for result in individualResults {
82 | print(result.source)
83 | print(result.data)
84 | print()
85 | }
86 | }
87 | }
88 |
89 | print("\n-----Single Body Phone Results-----\n")
90 |
91 | if let phoneResults = dataDetectorService.extractPhoneNumbers(fromTextBody: phone) {
92 | for result in phoneResults {
93 | print(result.source)
94 | print(result.data)
95 | print()
96 | }
97 | }
98 |
99 | print("\n-----Multiple Bodies Phone Results-----\n")
100 |
101 | if let combinedPhoneResults = dataDetectorService.extractPhoneNumbers(fromTextBodies: phones) {
102 | for individualResults in combinedPhoneResults {
103 | for result in individualResults {
104 | print(result.source)
105 | print(result.data)
106 | print()
107 | }
108 | }
109 | }
110 |
111 | print("\n-----Single Body Transit Results-----\n")
112 |
113 | if let transitResults = dataDetectorService.extractTransitInformation(fromTextBody: transit) {
114 | for result in transitResults {
115 | print(result.source)
116 | print(result.data)
117 | print()
118 | }
119 | }
120 |
121 | print("\n-----Multiple Bodies Transit Results-----\n")
122 |
123 | if let combinedTransitResults = dataDetectorService.extractTransitInformation(fromTextBodies: transits) {
124 | for individualResults in combinedTransitResults {
125 | for result in individualResults {
126 | print(result.source)
127 | print(result.data)
128 | print()
129 | }
130 | }
131 | }
132 |
133 | print("\n-----Basic Scenario Implementation Results-----\n")
134 |
135 | print(textBody.dates!)
136 | print(link.links!)
137 | print(transit.transitInfo!)
138 | print(address.addresses!)
139 | print(phone.phoneNumbers!)
140 |
141 | print("\n-----Mixed Request Results-----\n")
142 |
143 | let mixedText = "Call Mayank on +1123456789 on 2017/13/12 at 5pm when you reach 990 Belchertown Rd, Amherst, MA - 01440 USA."
144 |
145 | if let results = dataDetectorService.extractInformation(fromTextBody: mixedText, withResultTypes: .date, .address, .phoneNumber) {
146 | for result in results {
147 | switch result.dataType {
148 | case .date:
149 | let date = result.data as! Date
150 | print(date)
151 | break
152 | case .address:
153 | let address = result.data as! AddressInfo
154 | print(address)
155 | break
156 | case .phoneNumber:
157 | let phoneNumber = result.data as! String
158 | print(phoneNumber)
159 | break
160 | default:
161 | continue
162 | }
163 | }
164 | }
165 |
--------------------------------------------------------------------------------
/MKDataDetectorTests/AddressExtensionTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AddressExtensionTests.swift
3 | // MKDataDetector
4 | //
5 | // Created by Jeet Parte on 22/07/17.
6 | // Copyright 2017 Mayank Kumar. Available under the MIT license.
7 | //
8 |
9 | import XCTest
10 | import CoreLocation
11 | @testable import MKDataDetector
12 |
13 | class AddressTests: XCTestCase {
14 |
15 | let dataDetectorService = MKDataDetectorService()
16 | var textBody = "1800 Ellis St,San Francisco, CA 94102, United States"
17 | var textBodies = ["1800 Ellis St,San Francisco, CA 94102, United States", "133 Belchertown Rd Amherst MA - 01003 United States"]
18 | let type: NSTextCheckingResult.CheckingType = .address
19 | var nsDataDetector: NSDataDetector!
20 |
21 | override func setUp() {
22 | super.setUp()
23 | nsDataDetector = try! NSDataDetector(types: type.rawValue)
24 | }
25 |
26 | func testSingleTextBody() {
27 | let results = dataDetectorService.extractAddresses(fromTextBody: textBody)
28 | let expectedCount = nsDataDetector.numberOfMatches(in: textBody, range: NSMakeRange(0, textBody.utf16.count))
29 | if expectedCount > 0 {
30 | XCTAssertNotNil(results)
31 | XCTAssertEqual(results!.count, expectedCount)
32 | } else {
33 | XCTAssertNil(results)
34 | }
35 | }
36 |
37 | func testMultipleTextBodies() {
38 | let combinedResults = dataDetectorService.extractAddresses(fromTextBodies: textBodies)
39 |
40 | //Check no.of matches for all textbodies
41 | let mergedTextBody = concatenate(textBodies)
42 | let totalMatches = nsDataDetector.numberOfMatches(in: mergedTextBody, range: NSMakeRange(0, mergedTextBody.utf16.count))
43 | if totalMatches > 0 {
44 | XCTAssertNotNil(combinedResults)
45 | for individualResult in combinedResults! {
46 | XCTAssertFalse(individualResult.isEmpty)
47 |
48 | //Check no. of matches for a present textbody
49 | let source = individualResult.first!.source //grab the textbody
50 | let expectedCount = nsDataDetector.numberOfMatches(in: source, range: NSMakeRange(0, source.utf16.count))
51 | XCTAssertEqual(individualResult.count, expectedCount)
52 | }
53 | } else {
54 | XCTAssertNil(combinedResults)
55 | }
56 | }
57 |
58 | // func testLocationExtractionFromAddressCompletionBlock() {
59 | // //Test whether completion block is being executed
60 | // var completionBlockExecuted = false
61 | // let validAddress = "1800 Ellis St, San Francisco, CA 94102, United States"
62 | // dataDetectorService.extractLocation(fromAddress: validAddress) { (location) in
63 | // do {
64 | // completionBlockExecuted = true
65 | // XCTAssert(completionBlockExecuted)
66 | // }
67 | // }
68 | // }
69 |
70 | func testLocationExtractionValidAddress() {
71 | //Test valid address
72 | let validAddress = "1800 Ellis St, San Francisco, CA 94102, United States"
73 | dataDetectorService.extractLocation(fromAddress: validAddress) { location in
74 | XCTAssertNotNil(location)
75 | XCTAssertEqual(location!.coordinate.latitude, 37.7858, accuracy: 0.05)
76 | XCTAssertEqual(location!.coordinate.longitude, -122.4065, accuracy: 0.05)
77 | }
78 | }
79 |
80 | func testLocationExtractionInvalidAddress() {
81 | //Test invalid address
82 | let invalidAddress = "No man's land 🌎"
83 | dataDetectorService.extractLocation(fromAddress: invalidAddress) { location in
84 | XCTAssertNil(location)
85 | }
86 | }
87 |
88 |
89 | func testLocationExtractionValidAddressResult() {
90 | //Test valid address
91 | let validAddress = "1800 Ellis St, San Francisco, CA 94102, United States"
92 | let results = dataDetectorService.extractAddresses(fromTextBody: validAddress)
93 | let result = results!.first
94 | dataDetectorService.extractLocation(fromAnalysisResult: (result!)) { location in
95 | XCTAssertNotNil(location)
96 | XCTAssertEqual(location!.coordinate.latitude, 37.7858, accuracy: 0.05)
97 | XCTAssertEqual(location!.coordinate.longitude, -122.4065, accuracy: 0.05)
98 | }
99 | }
100 |
101 | func testLocationExtractionInvalidAddressResult() {
102 | //Test invalid address
103 | let invalidAddress = "Fake address: 1800 Calvin St, San Francisco, CA 94102, United Kingdom"
104 | let results = dataDetectorService.extractAddresses(fromTextBody: invalidAddress)
105 | let result = results!.first
106 | dataDetectorService.extractLocation(fromAnalysisResult: (result!)) { location in
107 | XCTAssertNil(location)
108 | }
109 | }
110 |
111 | func testLocationExtractionPlaceMark() {
112 | //placemark nil, error nil
113 | let location = dataDetectorService.extractLocation(fromPlacemarks: nil, withError: nil)
114 | XCTAssertNil(location)
115 |
116 | //placemark nil, error not nil
117 | let error = CLError(_nsError: NSError())
118 | let locationII = dataDetectorService.extractLocation(fromPlacemarks: nil, withError: error)
119 | XCTAssertNil(locationII)
120 |
121 | //placemark not nil, error nil
122 | let geocoder = CLGeocoder()
123 | let validAddress = "1800 Ellis St, San Francisco, CA 94102, United States"
124 | geocoder.geocodeAddressString(validAddress) { (placemarks, error) in
125 | XCTAssertNil(error)
126 | let locationIII = self.dataDetectorService.extractLocation(fromPlacemarks: placemarks, withError: nil)
127 | XCTAssertNotNil(locationIII)
128 | }
129 | }
130 | }
131 |
132 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 | # **MKDataDetector**
3 | [](https://travis-ci.org/mayankk2308/mkdatadetector)
4 | 
5 |
6 | A simple convenience wrapper in Swift for data detection from natural language text that organizes data extraction and handling.
7 |
8 | ## Purpose
9 |
10 | `MKDataDetector` streamlines `NSDataDetector` and builds on it with additional supporting capabilities that use information effectively.
11 |
12 | ## Requirements
13 |
14 | * Swift 3.1+
15 | * macOS 10.9+
16 | * iOS 8.0+
17 | * tvOS 9.0+
18 | * watchOS 2.0+
19 |
20 | ## Installation
21 |
22 | There are multiple installation options to choose from.
23 |
24 | ### CocoaPods (Preferred)
25 |
26 | To install via CocoaPods, add the following line to your **Podfile**:
27 | ```ruby
28 | pod 'MKDataDetector'
29 | ```
30 |
31 | ### Carthage
32 |
33 | To install via Carthage, add the following line to your **Cartfile** along with the specific version of the framework you might prefer:
34 | ```
35 | github "mayankk2308/mkdatadetector-swift"
36 | ```
37 |
38 | ### Manual
39 |
40 | 1. Create a submodule in your project directory: `git submodule add https://github.com/mayankk2308/mkdatadetector-swift.git`
41 | 2. Open the submodule directory and drag the **.xcodeproj** file into your project.
42 | 3. Add **MKDataDetector.framework** to your target's _Link Binary with Libraries_ **Build Phase**.
43 | 4. You can now use the framework by importing it.
44 |
45 | ## Usage
46 |
47 | `MKDataDetectorService` is packaged as a set of extensions that compartmentalize its following capabilities:
48 |
49 | * Date - date extraction
50 | * Address - address extraction
51 | * Link - link extraction
52 | * Phone Number - phone number detection
53 | * Transit Information - flight information extraction, etc.
54 |
55 | In addition to extracting these features, the framework also provides convenience functions to manipulate and organize this data.
56 |
57 | To import and use the `MKDataDetectorService` class, add the following statement to your `.swift` file:
58 | ```swift
59 | import MKDataDetector
60 | ```
61 |
62 | You can use basic functionality as an extension of `String`:
63 | ```swift
64 | let testString: String = "sampleText"
65 |
66 | // extract Dates
67 | if let dates: [Date] = testString.dates {
68 | print(dates)
69 | }
70 |
71 | // extract Links
72 | if let links: [URL] = testString.links {
73 | print(links)
74 | }
75 | ```
76 |
77 | Similar extensions exist for addresses, transit, and phone numbers as well. For more informative results, you may want to initialize the service.
78 |
79 | ### Initialization
80 |
81 | You can declare an instance as follows:
82 | ```swift
83 | let dataDetectorService: MKDataDetectorService = MKDataDetectorService()
84 | ```
85 |
86 | ### Result Handling
87 |
88 | A generic set of `AnalysisResult` structures is consistently returned for extraction/analysis results. An enumeration called `ResultType` is also included for identification of results.
89 |
90 | `AnalysisResult` contains **5** fields:
91 | * Source (`source`) - the source/original complete `String` from which data was detected
92 | * Match Range (`rangeInSource`) - the `NSRange` of the matched string in the original string
93 | * Data String (`dataString`) - the substring from which `data` was matched
94 | * Data Type (`dataType`) - the type `ResultType` of data returned
95 | * Data (`data`) - the data `T` extracted from the source input
96 |
97 | The generic struct has a `typealias` per result type:
98 | * `DateAnalysisResult` - for `AnalysisResult`
99 | * `URLAnalysisResult` - for `AnalysisResult`
100 | * `AddressAnalysisResult` - for `AnalysisResult`
101 | * `PhoneNumberAnalysisResult` - for `AnalysisResult`
102 | * `TransitAnalysisResult` - for `AnalysisResult`
103 |
104 | The address and transit information results are structures (`[String : String]`) typealiased as `AddressInfo` and `TransitInfo`. `Address` and `Transit` **_structs_** with their associated dictionary keys make information lookup simple. For example, to access the zip-code in an extracted address, simply use the key `Address.zip`.
105 |
106 | ### Implementation
107 |
108 | To extract dates from some text (`String`):
109 | ```swift
110 | if let results = dataDetectorService.extractDates(withTextBody: sampleTextBody) {
111 | for result in results {
112 | print(result.source)
113 | print(result.data)
114 | // do some stuff
115 | }
116 | }
117 | ```
118 | For a given `textBody`, the `dataDetectorService` returns an array of `DateAnalysisResult` objects.
119 |
120 | To extract dates from multiple sources of text (`[String]`):
121 | ```swift
122 | if let combinedResults = dataDetectorService.extractDates(withTextBodies: [sampleText, sampleText, ...]) {
123 | for individualResults in combinedResults {
124 | for result in individualResults {
125 | print(result.source)
126 | print(result.data)
127 | // do some stuff
128 | }
129 | }
130 | }
131 | ```
132 | For given `textBodies`, the `dataDetectorService` returns an array of `[DateAnalysisResult]` objects.
133 |
134 | The extraction process is uniform for other types of data features such as phone numbers, addresses, links, and more.
135 |
136 | In cases where detection with multiple `ResultType`s are required, the following implementation may be used:
137 | ```swift
138 | if let results = dataDetectorService.extractInformation(fromTextBody textBody: String, withResultTypes: .date, .address ...) {
139 | for result in results {
140 | switch result.dataType {
141 | case .date:
142 | // force cast as DateAnalysisResult - create and save events, etc.
143 | case .address:
144 | // force cast as AddressAnalysisResult - get location, etc.
145 | .
146 | .
147 | // for all result types you are concerned with, i.e, your input parameters
148 | }
149 | }
150 | }
151 | ```
152 |
153 | An implementation for extracting multiple types from multiple text bodies is also included.
154 |
155 | ### Additional Capabilities
156 |
157 | `MKDataDetector` also provides handy convenience functions to use detected information.
158 |
159 | To retrieve precise **location information** from a **valid** `String` address:
160 | ```swift
161 | dataDetectorService.extractLocation(fromAddress: sampleText) { location in
162 | if extractedLocation = location {
163 | // CLLocation object available, requires importing 'CoreLocation'
164 | }
165 | }
166 | ```
167 |
168 | Alternatively, if you already have an `AddressAnalysisResult`:
169 | ```swift
170 | dataDetectorService.extractLocation(fromAnalysisResult: sampleAnalysisResult) { location in
171 | if extractedLocation = location {
172 | // CLLocation object available, requires importing 'CoreLocation'
173 | }
174 | }
175 | ```
176 |
177 | For **calendar integration**, you can easily create an `EKEvent` from a `DateAnalysisResult`:
178 | ```swift
179 | let event: EKEvent = dataDetectorService.generateEvent(fromEventStore: sampleEventStore, withAnalysisResult: sampleResult)
180 | ```
181 | A __*withEndDate*__ parameter, not shown above, is optional. Not providing a value defaults the event to end after an hour.
182 |
183 | A more generic event generator is also available, which may be preferred if 100% event naming consistency is expected. Automatic processing of event names from `DateAnalysisResult` objects needs more testing.
184 |
185 | Given a set of retrieved `AnalysisResult` (the default result type for any extraction operation), you can generate **colored attributed texts**:
186 | ```swift
187 | if let attributedText = dataDetectorService.attributedText(fromAnalysisResults: sampleResults, withColor: UIColor.blue.cgcolor) {
188 | // set UI component
189 | }
190 | ```
191 |
192 | More convenience capabilities will be incorporated into future releases.
193 |
194 | ### Examples
195 |
196 | Consider the following inputs:
197 | ```swift
198 | let meeting: String = "Meeting at 9pm tomorrow"
199 | let party: String = "Party next Friday at 8pm"
200 | ```
201 |
202 | Extracting dates using `dataDetectorService`, we receive the following output for `meeting`:
203 | * `source` = *"Meeting at 9pm tomorrow"*
204 | * `sourceInRange` = `NSRange` of the match *"at 9pm tomorrow"*
205 | * `dataString` = the match substring *"at 9pm tomorrow"*
206 | * `data` = equivalent `Date` object, specifying source date relative to the current date/time on the device
207 |
208 | The output is similar for `party`:
209 | * `source` = *"Party next Friday at 8pm"*
210 | * `sourceInRange` = `NSRange` of the match *"next Friday at 8pm"*
211 | * `dataString` = the match substring *"next Friday at 8pm"*
212 | * `data` = equivalent `Date` object, specifying source date relative to the current date/time on the device
213 |
214 | The output format will be uniform for other types of data features as well, with the `data` field returning objects of the appropriate type in each case.
215 |
216 | You can easily make use of this data, for instance, by generating an event:
217 | ```swift
218 | let meetingEvent = dataDetectorService.generateEvent(withEventStore: someEventStore, withAnalysisResult: meetingAnalysisResult)
219 | // creates an event detailing a meeting for 9pm tomorrow, lasting an hour
220 |
221 | let partyEvent = dataDetectorService.generateEvent(withEventStore: someEventStore, withAnalysisResult: partyAnalysisResult)
222 | // creates an event detailing a party at 8pm next Friday, lasting an hour
223 | ```
224 |
225 | Assume that the `meeting` text was embedded in a `UILabel` called `meetingLabel`. It was also expanded to add *" and next Friday at 5pm"*. You can update the label to display the **multiple detected** parts of the text:
226 | ```swift
227 | if let attributedText = dataDetectorService.attributedText(withAnalysisResults: meetingAnalysisResult, withColor: UIColor.purple.cgcolor) {
228 | meetingLabel.attributedText = attributedText
229 | }
230 | ```
231 |
232 | `meetingLabel` will now display the original text with the detected information in purple (bold here):
233 | *"Meeting __at 9pm tomorrow__ and __next Friday at 5pm__"*.
234 |
235 | ### Limitations
236 |
237 | Apple's documentation on `NSDataDetector` states that the class can currently match dates, addresses, links, phone numbers and transit information, and not other present properties such as grammar and spelling.
238 |
239 | Additionally, `NSDataDetector` does not detect:
240 | * the **name**, **job title**, **organization** & **phone number** components of an address, although keys for the same are provided within the original API
241 | * the **airline name** component for transit information, although a key for this is available in the original API
242 |
243 | ## Contact
244 |
245 | You can contact:
246 | * [Mayank Kumar](https://github.com/mayankk2308) - via [email](mailto:mayankk2308@gmail.com) or [LinkedIn](https://www.linkedin.com/in/mayank-kumar-478245b1/)
247 | * [Jeet Parte](https://github.com/jeetparte) - via [email](mailto:jeetparte@gmail.com)
248 |
249 | for any inquires or community-related issues.
250 |
251 | ## License
252 |
253 | This project is available under the [MIT](https://github.com/mayankk2308/mkdatadetector-swift/blob/master/LICENSE.md) license.
254 |
--------------------------------------------------------------------------------
/MKDataDetector.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | A8574ABE1F1B4EB900493423 /* StringAccess.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8574ABD1F1B4EB900493423 /* StringAccess.swift */; };
11 | A85D37F11F13677600F5DDF1 /* AnalysisResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = A85D37F01F13677600F5DDF1 /* AnalysisResult.swift */; };
12 | A874CD001F10F3260022AA85 /* MKDataDetector.h in Headers */ = {isa = PBXBuildFile; fileRef = A874CCFE1F10F3260022AA85 /* MKDataDetector.h */; settings = {ATTRIBUTES = (Public, ); }; };
13 | A874CD0A1F11011E0022AA85 /* MKDataDetectorService.swift in Sources */ = {isa = PBXBuildFile; fileRef = A874CD091F11011E0022AA85 /* MKDataDetectorService.swift */; };
14 | A887490B1F6C538500013F25 /* MKDataDetector.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A874CCFB1F10F3260022AA85 /* MKDataDetector.framework */; };
15 | A88749111F6C53D500013F25 /* MultiDataTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C954BEB51F248A950030D00B /* MultiDataTests.swift */; };
16 | A88749121F6C53D500013F25 /* StringAccessTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C954BEB31F248A850030D00B /* StringAccessTests.swift */; };
17 | A88749131F6C53D500013F25 /* TransitInformationExtensionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C954BEB11F2488C80030D00B /* TransitInformationExtensionTests.swift */; };
18 | A88749141F6C53D500013F25 /* PhoneNumberExtensionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C954BEAF1F2487DD0030D00B /* PhoneNumberExtensionTests.swift */; };
19 | A88749151F6C53D500013F25 /* LinkExtensionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C94DDE261F23A1B80049D156 /* LinkExtensionTests.swift */; };
20 | A88749161F6C53D500013F25 /* AddressExtensionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C94DDE241F23A1010049D156 /* AddressExtensionTests.swift */; };
21 | A88749171F6C53D500013F25 /* DateExtensionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C94DDE221F239E8F0049D156 /* DateExtensionTests.swift */; };
22 | A88749181F6C53D500013F25 /* TestUtilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = C94DDE281F23B03F0049D156 /* TestUtilities.swift */; };
23 | A88DD8331F123B8A001D067A /* Date.swift in Sources */ = {isa = PBXBuildFile; fileRef = A88DD8321F123B8A001D067A /* Date.swift */; };
24 | A88DD8371F123BA7001D067A /* Address.swift in Sources */ = {isa = PBXBuildFile; fileRef = A88DD8361F123BA7001D067A /* Address.swift */; };
25 | A88DD83B1F123E83001D067A /* PhoneNumber.swift in Sources */ = {isa = PBXBuildFile; fileRef = A88DD83A1F123E83001D067A /* PhoneNumber.swift */; };
26 | A88DD8411F12403C001D067A /* Link.swift in Sources */ = {isa = PBXBuildFile; fileRef = A88DD8401F12403C001D067A /* Link.swift */; };
27 | A88DD8451F12407C001D067A /* TransitInformation.swift in Sources */ = {isa = PBXBuildFile; fileRef = A88DD8441F12407C001D067A /* TransitInformation.swift */; };
28 | A8A061A41F1FCB3400EF542E /* AliasManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8A061A31F1FCB3400EF542E /* AliasManager.swift */; };
29 | A8B869171F1D138C003FC75A /* MultiData.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8B869161F1D138C003FC75A /* MultiData.swift */; };
30 | A8D560201F139F3E00553632 /* UtilityExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8D5601F1F139F3E00553632 /* UtilityExtensions.swift */; };
31 | A8EC757C1F14D67400D15369 /* CoreKeys.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8EC757B1F14D67400D15369 /* CoreKeys.swift */; };
32 | A8F07E341F14724700E1A5C5 /* ResultType.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8F07E331F14724700E1A5C5 /* ResultType.swift */; };
33 | /* End PBXBuildFile section */
34 |
35 | /* Begin PBXContainerItemProxy section */
36 | A887490C1F6C538500013F25 /* PBXContainerItemProxy */ = {
37 | isa = PBXContainerItemProxy;
38 | containerPortal = A874CCF21F10F3260022AA85 /* Project object */;
39 | proxyType = 1;
40 | remoteGlobalIDString = A874CCFA1F10F3260022AA85;
41 | remoteInfo = MKDataDetector;
42 | };
43 | /* End PBXContainerItemProxy section */
44 |
45 | /* Begin PBXFileReference section */
46 | A8574ABD1F1B4EB900493423 /* StringAccess.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StringAccess.swift; sourceTree = ""; };
47 | A85D37F01F13677600F5DDF1 /* AnalysisResult.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AnalysisResult.swift; sourceTree = ""; };
48 | A874CCFB1F10F3260022AA85 /* MKDataDetector.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = MKDataDetector.framework; sourceTree = BUILT_PRODUCTS_DIR; };
49 | A874CCFE1F10F3260022AA85 /* MKDataDetector.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MKDataDetector.h; sourceTree = ""; };
50 | A874CCFF1F10F3260022AA85 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
51 | A874CD091F11011E0022AA85 /* MKDataDetectorService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MKDataDetectorService.swift; sourceTree = ""; };
52 | A88749061F6C538500013F25 /* MKDataDetectorTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MKDataDetectorTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
53 | A887490A1F6C538500013F25 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
54 | A88DD8321F123B8A001D067A /* Date.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Date.swift; sourceTree = ""; };
55 | A88DD8361F123BA7001D067A /* Address.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Address.swift; sourceTree = ""; };
56 | A88DD83A1F123E83001D067A /* PhoneNumber.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PhoneNumber.swift; sourceTree = ""; };
57 | A88DD8401F12403C001D067A /* Link.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Link.swift; sourceTree = ""; };
58 | A88DD8441F12407C001D067A /* TransitInformation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransitInformation.swift; sourceTree = ""; };
59 | A8A061A31F1FCB3400EF542E /* AliasManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AliasManager.swift; sourceTree = ""; };
60 | A8B869161F1D138C003FC75A /* MultiData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MultiData.swift; sourceTree = ""; };
61 | A8D5601F1F139F3E00553632 /* UtilityExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UtilityExtensions.swift; sourceTree = ""; };
62 | A8EC757B1F14D67400D15369 /* CoreKeys.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CoreKeys.swift; sourceTree = ""; };
63 | A8F07E331F14724700E1A5C5 /* ResultType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ResultType.swift; sourceTree = ""; };
64 | C94DDE221F239E8F0049D156 /* DateExtensionTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DateExtensionTests.swift; sourceTree = ""; };
65 | C94DDE241F23A1010049D156 /* AddressExtensionTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AddressExtensionTests.swift; sourceTree = ""; };
66 | C94DDE261F23A1B80049D156 /* LinkExtensionTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LinkExtensionTests.swift; sourceTree = ""; };
67 | C94DDE281F23B03F0049D156 /* TestUtilities.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestUtilities.swift; sourceTree = ""; };
68 | C954BEAF1F2487DD0030D00B /* PhoneNumberExtensionTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PhoneNumberExtensionTests.swift; sourceTree = ""; };
69 | C954BEB11F2488C80030D00B /* TransitInformationExtensionTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransitInformationExtensionTests.swift; sourceTree = ""; };
70 | C954BEB31F248A850030D00B /* StringAccessTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StringAccessTests.swift; sourceTree = ""; };
71 | C954BEB51F248A950030D00B /* MultiDataTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MultiDataTests.swift; sourceTree = ""; };
72 | /* End PBXFileReference section */
73 |
74 | /* Begin PBXFrameworksBuildPhase section */
75 | A874CCF71F10F3260022AA85 /* Frameworks */ = {
76 | isa = PBXFrameworksBuildPhase;
77 | buildActionMask = 2147483647;
78 | files = (
79 | );
80 | runOnlyForDeploymentPostprocessing = 0;
81 | };
82 | A88749031F6C538500013F25 /* Frameworks */ = {
83 | isa = PBXFrameworksBuildPhase;
84 | buildActionMask = 2147483647;
85 | files = (
86 | A887490B1F6C538500013F25 /* MKDataDetector.framework in Frameworks */,
87 | );
88 | runOnlyForDeploymentPostprocessing = 0;
89 | };
90 | /* End PBXFrameworksBuildPhase section */
91 |
92 | /* Begin PBXGroup section */
93 | A874CCF11F10F3260022AA85 = {
94 | isa = PBXGroup;
95 | children = (
96 | A874CCFD1F10F3260022AA85 /* MKDataDetector */,
97 | A895A6F41F1A358E006A6C35 /* MKDataDetectorTests */,
98 | A874CCFC1F10F3260022AA85 /* Products */,
99 | );
100 | sourceTree = "";
101 | };
102 | A874CCFC1F10F3260022AA85 /* Products */ = {
103 | isa = PBXGroup;
104 | children = (
105 | A874CCFB1F10F3260022AA85 /* MKDataDetector.framework */,
106 | A88749061F6C538500013F25 /* MKDataDetectorTests.xctest */,
107 | );
108 | name = Products;
109 | sourceTree = "";
110 | };
111 | A874CCFD1F10F3260022AA85 /* MKDataDetector */ = {
112 | isa = PBXGroup;
113 | children = (
114 | A88DD8381F123BB3001D067A /* Core Services */,
115 | A8EC75791F14D56900D15369 /* Data Management */,
116 | A88DD8391F123BC7001D067A /* Extensions */,
117 | A874CD061F10F3370022AA85 /* Configuration */,
118 | );
119 | path = MKDataDetector;
120 | sourceTree = "";
121 | };
122 | A874CD061F10F3370022AA85 /* Configuration */ = {
123 | isa = PBXGroup;
124 | children = (
125 | A874CCFE1F10F3260022AA85 /* MKDataDetector.h */,
126 | A874CCFF1F10F3260022AA85 /* Info.plist */,
127 | );
128 | name = Configuration;
129 | sourceTree = "";
130 | };
131 | A88DD8381F123BB3001D067A /* Core Services */ = {
132 | isa = PBXGroup;
133 | children = (
134 | A874CD091F11011E0022AA85 /* MKDataDetectorService.swift */,
135 | A8D5601F1F139F3E00553632 /* UtilityExtensions.swift */,
136 | );
137 | name = "Core Services";
138 | sourceTree = "";
139 | };
140 | A88DD8391F123BC7001D067A /* Extensions */ = {
141 | isa = PBXGroup;
142 | children = (
143 | A88DD8321F123B8A001D067A /* Date.swift */,
144 | A88DD8361F123BA7001D067A /* Address.swift */,
145 | A88DD8401F12403C001D067A /* Link.swift */,
146 | A88DD83A1F123E83001D067A /* PhoneNumber.swift */,
147 | A88DD8441F12407C001D067A /* TransitInformation.swift */,
148 | A8574ABD1F1B4EB900493423 /* StringAccess.swift */,
149 | A8B869161F1D138C003FC75A /* MultiData.swift */,
150 | );
151 | name = Extensions;
152 | sourceTree = "";
153 | };
154 | A895A6F41F1A358E006A6C35 /* MKDataDetectorTests */ = {
155 | isa = PBXGroup;
156 | children = (
157 | C954BEB51F248A950030D00B /* MultiDataTests.swift */,
158 | C954BEB31F248A850030D00B /* StringAccessTests.swift */,
159 | C954BEB11F2488C80030D00B /* TransitInformationExtensionTests.swift */,
160 | C954BEAF1F2487DD0030D00B /* PhoneNumberExtensionTests.swift */,
161 | C94DDE261F23A1B80049D156 /* LinkExtensionTests.swift */,
162 | C94DDE241F23A1010049D156 /* AddressExtensionTests.swift */,
163 | C94DDE221F239E8F0049D156 /* DateExtensionTests.swift */,
164 | C94DDE281F23B03F0049D156 /* TestUtilities.swift */,
165 | A887490A1F6C538500013F25 /* Info.plist */,
166 | );
167 | path = MKDataDetectorTests;
168 | sourceTree = "";
169 | };
170 | A8EC75791F14D56900D15369 /* Data Management */ = {
171 | isa = PBXGroup;
172 | children = (
173 | A85D37F01F13677600F5DDF1 /* AnalysisResult.swift */,
174 | A8A061A31F1FCB3400EF542E /* AliasManager.swift */,
175 | A8F07E331F14724700E1A5C5 /* ResultType.swift */,
176 | A8EC757B1F14D67400D15369 /* CoreKeys.swift */,
177 | );
178 | name = "Data Management";
179 | sourceTree = "";
180 | };
181 | /* End PBXGroup section */
182 |
183 | /* Begin PBXHeadersBuildPhase section */
184 | A874CCF81F10F3260022AA85 /* Headers */ = {
185 | isa = PBXHeadersBuildPhase;
186 | buildActionMask = 2147483647;
187 | files = (
188 | A874CD001F10F3260022AA85 /* MKDataDetector.h in Headers */,
189 | );
190 | runOnlyForDeploymentPostprocessing = 0;
191 | };
192 | /* End PBXHeadersBuildPhase section */
193 |
194 | /* Begin PBXNativeTarget section */
195 | A874CCFA1F10F3260022AA85 /* MKDataDetector */ = {
196 | isa = PBXNativeTarget;
197 | buildConfigurationList = A874CD031F10F3260022AA85 /* Build configuration list for PBXNativeTarget "MKDataDetector" */;
198 | buildPhases = (
199 | A874CCF61F10F3260022AA85 /* Sources */,
200 | A874CCF71F10F3260022AA85 /* Frameworks */,
201 | A874CCF81F10F3260022AA85 /* Headers */,
202 | A874CCF91F10F3260022AA85 /* Resources */,
203 | );
204 | buildRules = (
205 | );
206 | dependencies = (
207 | );
208 | name = MKDataDetector;
209 | productName = MKDataDetector;
210 | productReference = A874CCFB1F10F3260022AA85 /* MKDataDetector.framework */;
211 | productType = "com.apple.product-type.framework";
212 | };
213 | A88749051F6C538500013F25 /* MKDataDetectorTests */ = {
214 | isa = PBXNativeTarget;
215 | buildConfigurationList = A887490E1F6C538500013F25 /* Build configuration list for PBXNativeTarget "MKDataDetectorTests" */;
216 | buildPhases = (
217 | A88749021F6C538500013F25 /* Sources */,
218 | A88749031F6C538500013F25 /* Frameworks */,
219 | A88749041F6C538500013F25 /* Resources */,
220 | );
221 | buildRules = (
222 | );
223 | dependencies = (
224 | A887490D1F6C538500013F25 /* PBXTargetDependency */,
225 | );
226 | name = MKDataDetectorTests;
227 | productName = MKDataDetectorTests;
228 | productReference = A88749061F6C538500013F25 /* MKDataDetectorTests.xctest */;
229 | productType = "com.apple.product-type.bundle.unit-test";
230 | };
231 | /* End PBXNativeTarget section */
232 |
233 | /* Begin PBXProject section */
234 | A874CCF21F10F3260022AA85 /* Project object */ = {
235 | isa = PBXProject;
236 | attributes = {
237 | LastSwiftUpdateCheck = 0900;
238 | LastUpgradeCheck = 1100;
239 | ORGANIZATIONNAME = "Mayank Kumar";
240 | TargetAttributes = {
241 | A874CCFA1F10F3260022AA85 = {
242 | CreatedOnToolsVersion = 8.3.3;
243 | DevelopmentTeam = YF7XP6ZYSQ;
244 | LastSwiftMigration = 1100;
245 | ProvisioningStyle = Automatic;
246 | };
247 | A88749051F6C538500013F25 = {
248 | CreatedOnToolsVersion = 9.0;
249 | DevelopmentTeam = YF7XP6ZYSQ;
250 | LastSwiftMigration = 1100;
251 | ProvisioningStyle = Automatic;
252 | };
253 | };
254 | };
255 | buildConfigurationList = A874CCF51F10F3260022AA85 /* Build configuration list for PBXProject "MKDataDetector" */;
256 | compatibilityVersion = "Xcode 3.2";
257 | developmentRegion = en;
258 | hasScannedForEncodings = 0;
259 | knownRegions = (
260 | en,
261 | Base,
262 | );
263 | mainGroup = A874CCF11F10F3260022AA85;
264 | productRefGroup = A874CCFC1F10F3260022AA85 /* Products */;
265 | projectDirPath = "";
266 | projectRoot = "";
267 | targets = (
268 | A874CCFA1F10F3260022AA85 /* MKDataDetector */,
269 | A88749051F6C538500013F25 /* MKDataDetectorTests */,
270 | );
271 | };
272 | /* End PBXProject section */
273 |
274 | /* Begin PBXResourcesBuildPhase section */
275 | A874CCF91F10F3260022AA85 /* Resources */ = {
276 | isa = PBXResourcesBuildPhase;
277 | buildActionMask = 2147483647;
278 | files = (
279 | );
280 | runOnlyForDeploymentPostprocessing = 0;
281 | };
282 | A88749041F6C538500013F25 /* Resources */ = {
283 | isa = PBXResourcesBuildPhase;
284 | buildActionMask = 2147483647;
285 | files = (
286 | );
287 | runOnlyForDeploymentPostprocessing = 0;
288 | };
289 | /* End PBXResourcesBuildPhase section */
290 |
291 | /* Begin PBXSourcesBuildPhase section */
292 | A874CCF61F10F3260022AA85 /* Sources */ = {
293 | isa = PBXSourcesBuildPhase;
294 | buildActionMask = 2147483647;
295 | files = (
296 | A88DD8331F123B8A001D067A /* Date.swift in Sources */,
297 | A8574ABE1F1B4EB900493423 /* StringAccess.swift in Sources */,
298 | A874CD0A1F11011E0022AA85 /* MKDataDetectorService.swift in Sources */,
299 | A8A061A41F1FCB3400EF542E /* AliasManager.swift in Sources */,
300 | A88DD83B1F123E83001D067A /* PhoneNumber.swift in Sources */,
301 | A88DD8371F123BA7001D067A /* Address.swift in Sources */,
302 | A88DD8451F12407C001D067A /* TransitInformation.swift in Sources */,
303 | A88DD8411F12403C001D067A /* Link.swift in Sources */,
304 | A8B869171F1D138C003FC75A /* MultiData.swift in Sources */,
305 | A85D37F11F13677600F5DDF1 /* AnalysisResult.swift in Sources */,
306 | A8D560201F139F3E00553632 /* UtilityExtensions.swift in Sources */,
307 | A8EC757C1F14D67400D15369 /* CoreKeys.swift in Sources */,
308 | A8F07E341F14724700E1A5C5 /* ResultType.swift in Sources */,
309 | );
310 | runOnlyForDeploymentPostprocessing = 0;
311 | };
312 | A88749021F6C538500013F25 /* Sources */ = {
313 | isa = PBXSourcesBuildPhase;
314 | buildActionMask = 2147483647;
315 | files = (
316 | A88749161F6C53D500013F25 /* AddressExtensionTests.swift in Sources */,
317 | A88749151F6C53D500013F25 /* LinkExtensionTests.swift in Sources */,
318 | A88749181F6C53D500013F25 /* TestUtilities.swift in Sources */,
319 | A88749121F6C53D500013F25 /* StringAccessTests.swift in Sources */,
320 | A88749171F6C53D500013F25 /* DateExtensionTests.swift in Sources */,
321 | A88749141F6C53D500013F25 /* PhoneNumberExtensionTests.swift in Sources */,
322 | A88749111F6C53D500013F25 /* MultiDataTests.swift in Sources */,
323 | A88749131F6C53D500013F25 /* TransitInformationExtensionTests.swift in Sources */,
324 | );
325 | runOnlyForDeploymentPostprocessing = 0;
326 | };
327 | /* End PBXSourcesBuildPhase section */
328 |
329 | /* Begin PBXTargetDependency section */
330 | A887490D1F6C538500013F25 /* PBXTargetDependency */ = {
331 | isa = PBXTargetDependency;
332 | target = A874CCFA1F10F3260022AA85 /* MKDataDetector */;
333 | targetProxy = A887490C1F6C538500013F25 /* PBXContainerItemProxy */;
334 | };
335 | /* End PBXTargetDependency section */
336 |
337 | /* Begin XCBuildConfiguration section */
338 | A874CD011F10F3260022AA85 /* Debug */ = {
339 | isa = XCBuildConfiguration;
340 | buildSettings = {
341 | ALWAYS_SEARCH_USER_PATHS = NO;
342 | ARCHS = "$(ARCHS_STANDARD)";
343 | CLANG_ANALYZER_NONNULL = YES;
344 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
345 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
346 | CLANG_CXX_LIBRARY = "libc++";
347 | CLANG_ENABLE_MODULES = YES;
348 | CLANG_ENABLE_OBJC_ARC = YES;
349 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
350 | CLANG_WARN_BOOL_CONVERSION = YES;
351 | CLANG_WARN_COMMA = YES;
352 | CLANG_WARN_CONSTANT_CONVERSION = YES;
353 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
354 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
355 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
356 | CLANG_WARN_EMPTY_BODY = YES;
357 | CLANG_WARN_ENUM_CONVERSION = YES;
358 | CLANG_WARN_INFINITE_RECURSION = YES;
359 | CLANG_WARN_INT_CONVERSION = YES;
360 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
361 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
362 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
363 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
364 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
365 | CLANG_WARN_STRICT_PROTOTYPES = YES;
366 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
367 | CLANG_WARN_UNREACHABLE_CODE = YES;
368 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
369 | "CODE_SIGN_IDENTITY[sdk=*]" = "iPhone Developer";
370 | COPY_PHASE_STRIP = NO;
371 | CURRENT_PROJECT_VERSION = 1;
372 | DEBUG_INFORMATION_FORMAT = dwarf;
373 | ENABLE_STRICT_OBJC_MSGSEND = YES;
374 | ENABLE_TESTABILITY = YES;
375 | GCC_C_LANGUAGE_STANDARD = gnu99;
376 | GCC_DYNAMIC_NO_PIC = NO;
377 | GCC_NO_COMMON_BLOCKS = YES;
378 | GCC_OPTIMIZATION_LEVEL = 0;
379 | GCC_PREPROCESSOR_DEFINITIONS = (
380 | "DEBUG=1",
381 | "$(inherited)",
382 | );
383 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
384 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
385 | GCC_WARN_UNDECLARED_SELECTOR = YES;
386 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
387 | GCC_WARN_UNUSED_FUNCTION = YES;
388 | GCC_WARN_UNUSED_VARIABLE = YES;
389 | INFOPLIST_FILE = Source/Info.plist;
390 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
391 | MACOSX_DEPLOYMENT_TARGET = 10.9;
392 | MTL_ENABLE_DEBUG_INFO = YES;
393 | ONLY_ACTIVE_ARCH = YES;
394 | PRODUCT_NAME = MKDataDetector;
395 | SDKROOT = "";
396 | SUPPORTED_PLATFORMS = "macosx iphoneos iphonesimulator appletvsimulator appletvos watchsimulator watchos";
397 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
398 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
399 | SWIFT_VERSION = 3.0;
400 | TARGETED_DEVICE_FAMILY = "1,2";
401 | TVOS_DEPLOYMENT_TARGET = 9.0;
402 | VERSIONING_SYSTEM = "apple-generic";
403 | VERSION_INFO_PREFIX = "";
404 | WATCHOS_DEPLOYMENT_TARGET = 2.0;
405 | };
406 | name = Debug;
407 | };
408 | A874CD021F10F3260022AA85 /* Release */ = {
409 | isa = XCBuildConfiguration;
410 | buildSettings = {
411 | ALWAYS_SEARCH_USER_PATHS = NO;
412 | ARCHS = "$(ARCHS_STANDARD)";
413 | CLANG_ANALYZER_NONNULL = YES;
414 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
415 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
416 | CLANG_CXX_LIBRARY = "libc++";
417 | CLANG_ENABLE_MODULES = YES;
418 | CLANG_ENABLE_OBJC_ARC = YES;
419 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
420 | CLANG_WARN_BOOL_CONVERSION = YES;
421 | CLANG_WARN_COMMA = YES;
422 | CLANG_WARN_CONSTANT_CONVERSION = YES;
423 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
424 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
425 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
426 | CLANG_WARN_EMPTY_BODY = YES;
427 | CLANG_WARN_ENUM_CONVERSION = YES;
428 | CLANG_WARN_INFINITE_RECURSION = YES;
429 | CLANG_WARN_INT_CONVERSION = YES;
430 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
431 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
432 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
433 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
434 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
435 | CLANG_WARN_STRICT_PROTOTYPES = YES;
436 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
437 | CLANG_WARN_UNREACHABLE_CODE = YES;
438 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
439 | "CODE_SIGN_IDENTITY[sdk=*]" = "iPhone Developer";
440 | COPY_PHASE_STRIP = NO;
441 | CURRENT_PROJECT_VERSION = 1;
442 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
443 | ENABLE_NS_ASSERTIONS = NO;
444 | ENABLE_STRICT_OBJC_MSGSEND = YES;
445 | GCC_C_LANGUAGE_STANDARD = gnu99;
446 | GCC_NO_COMMON_BLOCKS = YES;
447 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
448 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
449 | GCC_WARN_UNDECLARED_SELECTOR = YES;
450 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
451 | GCC_WARN_UNUSED_FUNCTION = YES;
452 | GCC_WARN_UNUSED_VARIABLE = YES;
453 | INFOPLIST_FILE = Source/Info.plist;
454 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
455 | MACOSX_DEPLOYMENT_TARGET = 10.9;
456 | MTL_ENABLE_DEBUG_INFO = NO;
457 | PRODUCT_NAME = MKDataDetector;
458 | SDKROOT = "";
459 | SUPPORTED_PLATFORMS = "macosx iphoneos iphonesimulator appletvsimulator appletvos watchsimulator watchos";
460 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
461 | SWIFT_VERSION = 3.0;
462 | TARGETED_DEVICE_FAMILY = "1,2";
463 | TVOS_DEPLOYMENT_TARGET = 9.0;
464 | VALIDATE_PRODUCT = YES;
465 | VERSIONING_SYSTEM = "apple-generic";
466 | VERSION_INFO_PREFIX = "";
467 | WATCHOS_DEPLOYMENT_TARGET = 2.0;
468 | };
469 | name = Release;
470 | };
471 | A874CD041F10F3260022AA85 /* Debug */ = {
472 | isa = XCBuildConfiguration;
473 | buildSettings = {
474 | APPLICATION_EXTENSION_API_ONLY = YES;
475 | CLANG_ANALYZER_GCD_PERFORMANCE = YES;
476 | CLANG_ENABLE_MODULES = YES;
477 | CLANG_STATIC_ANALYZER_MODE = deep;
478 | CODE_SIGN_IDENTITY = "";
479 | CODE_SIGN_STYLE = Automatic;
480 | DEFINES_MODULE = YES;
481 | DEVELOPMENT_TEAM = YF7XP6ZYSQ;
482 | DYLIB_COMPATIBILITY_VERSION = 1;
483 | DYLIB_CURRENT_VERSION = 1;
484 | DYLIB_INSTALL_NAME_BASE = "@rpath";
485 | INFOPLIST_FILE = MKDataDetector/Info.plist;
486 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
487 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
488 | MARKETING_VERSION = 2.0.3;
489 | PRODUCT_BUNDLE_IDENTIFIER = com.mayank.MKDataDetector;
490 | PRODUCT_NAME = "$(TARGET_NAME)";
491 | PROVISIONING_PROFILE_SPECIFIER = "";
492 | RUN_CLANG_STATIC_ANALYZER = YES;
493 | SKIP_INSTALL = YES;
494 | SUPPORTED_PLATFORMS = "macosx iphoneos iphonesimulator appletvos appletvsimulator watchos watchsimulator";
495 | SWIFT_OPTIMIZATION_LEVEL = "-O";
496 | SWIFT_VERSION = 5.0;
497 | TARGETED_DEVICE_FAMILY = "1,2,3,4";
498 | };
499 | name = Debug;
500 | };
501 | A874CD051F10F3260022AA85 /* Release */ = {
502 | isa = XCBuildConfiguration;
503 | buildSettings = {
504 | APPLICATION_EXTENSION_API_ONLY = YES;
505 | CLANG_ANALYZER_GCD_PERFORMANCE = YES;
506 | CLANG_ENABLE_MODULES = YES;
507 | CLANG_STATIC_ANALYZER_MODE = deep;
508 | CODE_SIGN_IDENTITY = "";
509 | CODE_SIGN_STYLE = Automatic;
510 | DEFINES_MODULE = YES;
511 | DEVELOPMENT_TEAM = YF7XP6ZYSQ;
512 | DYLIB_COMPATIBILITY_VERSION = 1;
513 | DYLIB_CURRENT_VERSION = 1;
514 | DYLIB_INSTALL_NAME_BASE = "@rpath";
515 | INFOPLIST_FILE = MKDataDetector/Info.plist;
516 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
517 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
518 | MARKETING_VERSION = 2.0.3;
519 | PRODUCT_BUNDLE_IDENTIFIER = com.mayank.MKDataDetector;
520 | PRODUCT_NAME = "$(TARGET_NAME)";
521 | PROVISIONING_PROFILE_SPECIFIER = "";
522 | RUN_CLANG_STATIC_ANALYZER = YES;
523 | SKIP_INSTALL = YES;
524 | SUPPORTED_PLATFORMS = "macosx iphoneos iphonesimulator appletvos appletvsimulator watchos watchsimulator";
525 | SWIFT_VERSION = 5.0;
526 | TARGETED_DEVICE_FAMILY = "1,2,3,4";
527 | };
528 | name = Release;
529 | };
530 | A887490F1F6C538500013F25 /* Debug */ = {
531 | isa = XCBuildConfiguration;
532 | buildSettings = {
533 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
534 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
535 | CODE_SIGN_IDENTITY = "Mac Developer";
536 | CODE_SIGN_STYLE = Automatic;
537 | COMBINE_HIDPI_IMAGES = YES;
538 | DEVELOPMENT_TEAM = YF7XP6ZYSQ;
539 | GCC_C_LANGUAGE_STANDARD = gnu11;
540 | INFOPLIST_FILE = MKDataDetectorTests/Info.plist;
541 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
542 | MACOSX_DEPLOYMENT_TARGET = 10.13;
543 | PRODUCT_BUNDLE_IDENTIFIER = com.mayank.MKDataDetectorTests;
544 | PRODUCT_NAME = "$(TARGET_NAME)";
545 | PROVISIONING_PROFILE_SPECIFIER = "";
546 | SUPPORTED_PLATFORMS = "macosx iphoneos iphonesimulator appletvos appletvsimulator";
547 | SWIFT_VERSION = 5.0;
548 | TARGETED_DEVICE_FAMILY = "1,2,3";
549 | };
550 | name = Debug;
551 | };
552 | A88749101F6C538500013F25 /* Release */ = {
553 | isa = XCBuildConfiguration;
554 | buildSettings = {
555 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
556 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
557 | CODE_SIGN_IDENTITY = "Mac Developer";
558 | CODE_SIGN_STYLE = Automatic;
559 | COMBINE_HIDPI_IMAGES = YES;
560 | DEVELOPMENT_TEAM = YF7XP6ZYSQ;
561 | GCC_C_LANGUAGE_STANDARD = gnu11;
562 | INFOPLIST_FILE = MKDataDetectorTests/Info.plist;
563 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
564 | MACOSX_DEPLOYMENT_TARGET = 10.13;
565 | PRODUCT_BUNDLE_IDENTIFIER = com.mayank.MKDataDetectorTests;
566 | PRODUCT_NAME = "$(TARGET_NAME)";
567 | PROVISIONING_PROFILE_SPECIFIER = "";
568 | SUPPORTED_PLATFORMS = "macosx iphoneos iphonesimulator appletvos appletvsimulator";
569 | SWIFT_VERSION = 5.0;
570 | TARGETED_DEVICE_FAMILY = "1,2,3";
571 | };
572 | name = Release;
573 | };
574 | /* End XCBuildConfiguration section */
575 |
576 | /* Begin XCConfigurationList section */
577 | A874CCF51F10F3260022AA85 /* Build configuration list for PBXProject "MKDataDetector" */ = {
578 | isa = XCConfigurationList;
579 | buildConfigurations = (
580 | A874CD011F10F3260022AA85 /* Debug */,
581 | A874CD021F10F3260022AA85 /* Release */,
582 | );
583 | defaultConfigurationIsVisible = 0;
584 | defaultConfigurationName = Release;
585 | };
586 | A874CD031F10F3260022AA85 /* Build configuration list for PBXNativeTarget "MKDataDetector" */ = {
587 | isa = XCConfigurationList;
588 | buildConfigurations = (
589 | A874CD041F10F3260022AA85 /* Debug */,
590 | A874CD051F10F3260022AA85 /* Release */,
591 | );
592 | defaultConfigurationIsVisible = 0;
593 | defaultConfigurationName = Release;
594 | };
595 | A887490E1F6C538500013F25 /* Build configuration list for PBXNativeTarget "MKDataDetectorTests" */ = {
596 | isa = XCConfigurationList;
597 | buildConfigurations = (
598 | A887490F1F6C538500013F25 /* Debug */,
599 | A88749101F6C538500013F25 /* Release */,
600 | );
601 | defaultConfigurationIsVisible = 0;
602 | defaultConfigurationName = Release;
603 | };
604 | /* End XCConfigurationList section */
605 | };
606 | rootObject = A874CCF21F10F3260022AA85 /* Project object */;
607 | }
608 |
--------------------------------------------------------------------------------