From 77e9ccda4697d4cafb9854dffc88ffdbdbed7a71 Mon Sep 17 00:00:00 2001 From: Julien K Date: Tue, 16 Oct 2018 16:26:57 +0200 Subject: [PATCH 1/5] [DetectorType] add .mention, .hashtag and .custon() to detect your own pattern [DetectorType] Put mention and hashtag as var [CHANGELOG] Update [DetectorType] Update to have at least 4 characters and more [CHANGELOG] Update [DetectorType] [MessageLabel] run NSDataDetector once [MessageLabel] Run NSDataDetector once [Testing] Add testing and pass rangesOfDetectors as internal [Refactoring] [Filter] only .custom [Examples] --- CHANGELOG.md | 3 + .../Sources/Data Generation/SampleData.swift | 6 +- .../AdvancedExampleViewController.swift | 7 +- .../BasicExampleViewController.swift | 7 +- Sources/Models/DetectorType.swift | 38 +++++- Sources/Protocols/MessageLabelDelegate.swift | 25 ++++ Sources/Views/MessageLabel.swift | 93 ++++++++++++- Tests/ControllersTest/MessageLabelSpec.swift | 125 +++++++++++++++++- 8 files changed, 284 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4602998d..9e0b1329 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,9 @@ The changelog for `MessageKit`. Also see the [releases](https://github.com/Messa ### Added +- **Breaking Change** Added `.hashtag`, .`mention` to detect theses pattern inside the `messageLabel`. We also add `.custom(pattern: YOUR_PATTERN)` to `DetectorType` to manage and deal with your own regular expression. +[#913](https://github.com/MessageKit/MessageKit/pull/913) by [@JulienKode](https://github.com/julienkode). + - Added support for detection and handling of `NSLink`s inside of messages. [#815](https://github.com/MessageKit/MessageKit/pull/815) by [@jnic](https://github.com/jnic) diff --git a/Example/Sources/Data Generation/SampleData.swift b/Example/Sources/Data Generation/SampleData.swift index 63614a73..50b3dfa3 100644 --- a/Example/Sources/Data Generation/SampleData.swift +++ b/Example/Sources/Data Generation/SampleData.swift @@ -46,7 +46,7 @@ final internal class SampleData { let messageImages: [UIImage] = [#imageLiteral(resourceName: "img1"), #imageLiteral(resourceName: "img2")] - let messageTypes = ["Text", "Text", "Text", "AttributedText", "Location", "Photo", "Emoji", "Video", "URL", "Phone", "Custom"] + let messageTypes = ["Text", "Text", "Text", "AttributedText", "Location", "Photo", "Emoji", "Video", "URL", "Phone", "Mention", "Hashtag", "Custom"] let emojis = [ "👍", @@ -150,6 +150,10 @@ final internal class SampleData { return MockMessage(text: "https://github.com/MessageKit", sender: sender, messageId: uniqueID, date: date) case "Phone": return MockMessage(text: "123-456-7890", sender: sender, messageId: uniqueID, date: date) + case "Mention": + return MockMessage(text: "@messagekit", sender: sender, messageId: uniqueID, date: date) + case "Hashtag": + return MockMessage(text: "#messagekit", sender: sender, messageId: uniqueID, date: date) case "Custom": return MockMessage(custom: "Someone left the conversation", sender: system, messageId: uniqueID, date: date) default: diff --git a/Example/Sources/View Controllers/AdvancedExampleViewController.swift b/Example/Sources/View Controllers/AdvancedExampleViewController.swift index 32c8761f..3f3c8432 100644 --- a/Example/Sources/View Controllers/AdvancedExampleViewController.swift +++ b/Example/Sources/View Controllers/AdvancedExampleViewController.swift @@ -258,11 +258,14 @@ extension AdvancedExampleViewController: MessagesDisplayDelegate { } func detectorAttributes(for detector: DetectorType, and message: MessageType, at indexPath: IndexPath) -> [NSAttributedString.Key: Any] { - return MessageLabel.defaultAttributes + switch detector { + case .hashtag, .mention: return [.foregroundColor: UIColor.blue] + default: return MessageLabel.defaultAttributes + } } func enabledDetectors(for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> [DetectorType] { - return [.url, .address, .phoneNumber, .date, .transitInformation] + return [.url, .address, .phoneNumber, .date, .transitInformation, .mention, .hashtag] } // MARK: - All Messages diff --git a/Example/Sources/View Controllers/BasicExampleViewController.swift b/Example/Sources/View Controllers/BasicExampleViewController.swift index 0d7fc717..b2fd0e96 100644 --- a/Example/Sources/View Controllers/BasicExampleViewController.swift +++ b/Example/Sources/View Controllers/BasicExampleViewController.swift @@ -49,11 +49,14 @@ extension BasicExampleViewController: MessagesDisplayDelegate { } func detectorAttributes(for detector: DetectorType, and message: MessageType, at indexPath: IndexPath) -> [NSAttributedString.Key: Any] { - return MessageLabel.defaultAttributes + switch detector { + case .hashtag, .mention: return [.foregroundColor: UIColor.blue] + default: return MessageLabel.defaultAttributes + } } func enabledDetectors(for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> [DetectorType] { - return [.url, .address, .phoneNumber, .date, .transitInformation] + return [.url, .address, .phoneNumber, .date, .transitInformation, .mention, .hashtag] } // MARK: - All Messages diff --git a/Sources/Models/DetectorType.swift b/Sources/Models/DetectorType.swift index a49b7b6d..d3d43f2e 100644 --- a/Sources/Models/DetectorType.swift +++ b/Sources/Models/DetectorType.swift @@ -24,19 +24,19 @@ import Foundation -public enum DetectorType { +public enum DetectorType: Hashable { case address case date case phoneNumber case url case transitInformation + case custom(regex: NSRegularExpression) - // MARK: - Not supported yet - - //case mention - //case hashtag - //case custom + // swiftlint:disable force_try + public static var hashtag = DetectorType.custom(regex: try! NSRegularExpression(pattern: "#[a-zA-Z0-9]{4,}", options: [])) + public static var mention = DetectorType.custom(regex: try! NSRegularExpression(pattern: "@[a-zA-Z0-9]{4,}", options: [])) + // swiftlint:enable force_try internal var textCheckingType: NSTextCheckingResult.CheckingType { switch self { @@ -45,6 +45,32 @@ public enum DetectorType { case .phoneNumber: return .phoneNumber case .url: return .link case .transitInformation: return .transitInformation + case .custom: return .regularExpression + } + } + + /// Simply check if the detector type is a .custom + public var isCustom: Bool { + switch self { + case .custom: return true + default: return false + } + } + + ///The hashValue of the `DetectorType` so we can conform to `Hashable` and be sorted. + public var hashValue: Int { + return self.toInt() + } + + /// Return an 'Int' value for each `DetectorType` type so `DetectorType` can conform to `Hashable` + private func toInt() -> Int { + switch self { + case .address: return 0 + case .date: return 1 + case .phoneNumber: return 2 + case .url: return 3 + case .transitInformation: return 4 + case .custom(let regex): return regex.hashValue } } diff --git a/Sources/Protocols/MessageLabelDelegate.swift b/Sources/Protocols/MessageLabelDelegate.swift index e84ea622..46f5759d 100644 --- a/Sources/Protocols/MessageLabelDelegate.swift +++ b/Sources/Protocols/MessageLabelDelegate.swift @@ -56,6 +56,25 @@ public protocol MessageLabelDelegate: AnyObject { /// - Parameters: /// - transitInformation: The selected transit information. func didSelectTransitInformation(_ transitInformation: [String: String]) + + /// Triggered when a tap occurs on a mention + /// + /// - Parameters: + /// - mention: The selected mention + func didSelectMention(_ mention: String) + + /// Triggered when a tap occurs on a hashtag + /// + /// - Parameters: + /// - mention: The selected hashtag + func didSelectHashtag(_ hashtag: String) + + /// Triggered when a tap occurs on a custom regular expression + /// + /// - Parameters: + /// - pattern: the pattern of the regular expression + /// - match: part that match with the regular expression + func didSelectCustom(_ pattern: String, match: String?) } @@ -71,4 +90,10 @@ public extension MessageLabelDelegate { func didSelectTransitInformation(_ transitInformation: [String: String]) {} + func didSelectMention(_ mention: String) {} + + func didSelectHashtag(_ hashtag: String) {} + + func didSelectCustom(_ pattern: String, match: String?) {} + } diff --git a/Sources/Views/MessageLabel.swift b/Sources/Views/MessageLabel.swift index 9b04d5bd..d72a0ffc 100644 --- a/Sources/Views/MessageLabel.swift +++ b/Sources/Views/MessageLabel.swift @@ -49,7 +49,7 @@ open class MessageLabel: UILabel { return textStorage }() - private lazy var rangesForDetectors: [DetectorType: [(NSRange, MessageTextCheckingType)]] = [:] + internal lazy var rangesForDetectors: [DetectorType: [(NSRange, MessageTextCheckingType)]] = [:] private var isConfiguring: Bool = false @@ -134,6 +134,12 @@ open class MessageLabel: UILabel { open internal(set) var urlAttributes: [NSAttributedString.Key: Any] = defaultAttributes open internal(set) var transitInformationAttributes: [NSAttributedString.Key: Any] = defaultAttributes + + open internal(set) var hashtagAttributes: [NSAttributedString.Key: Any] = defaultAttributes + + open internal(set) var mentionAttributes: [NSAttributedString.Key: Any] = defaultAttributes + + open internal(set) var customAttributes: [NSRegularExpression: [NSAttributedString.Key: Any]] = [:] public func setAttributes(_ attributes: [NSAttributedString.Key: Any], detector: DetectorType) { switch detector { @@ -147,6 +153,12 @@ open class MessageLabel: UILabel { urlAttributes = attributes case .transitInformation: transitInformationAttributes = attributes + case .mention: + mentionAttributes = attributes + case .hashtag: + hashtagAttributes = attributes + case .custom(let regex): + customAttributes[regex] = attributes } if isConfiguring { attributesNeedUpdate = true @@ -276,6 +288,12 @@ open class MessageLabel: UILabel { return urlAttributes case .transitInformation: return transitInformationAttributes + case .mention: + return mentionAttributes + case .hashtag: + return hashtagAttributes + case .custom(let regex): + return customAttributes[regex] ?? MessageLabel.defaultAttributes } } @@ -301,10 +319,24 @@ open class MessageLabel: UILabel { private func parse(text: NSAttributedString) -> [NSTextCheckingResult] { guard enabledDetectors.isEmpty == false else { return [] } - let checkingTypes = enabledDetectors.reduce(0) { $0 | $1.textCheckingType.rawValue } - let detector = try? NSDataDetector(types: checkingTypes) let range = NSRange(location: 0, length: text.length) - let matches = detector?.matches(in: text.string, options: [], range: range) ?? [] + var matches = [NSTextCheckingResult]() + + // Get matches of all .custom DetectorType and add it to matches array + let regexs = enabledDetectors + .filter { $0.isCustom } + .map { parseForMatches(with: $0, in: text, for: range) } + .joined() + matches.append(contentsOf: regexs) + + // Get all Checking Types of detectors, except for .custom because they contain their own regex + let detectorCheckingTypes = enabledDetectors + .filter{ !$0.isCustom } + .reduce(0) { $0 | $1.textCheckingType.rawValue } + if detectorCheckingTypes > 0, let detector = try? NSDataDetector(types: detectorCheckingTypes) { + let detectorMatches = detector.matches(in: text.string, options: [], range: range) + matches.append(contentsOf: detectorMatches) + } guard enabledDetectors.contains(.url) else { return matches @@ -322,6 +354,24 @@ open class MessageLabel: UILabel { return results } + /** + Take a custom detector and apply the matching to the text + + - Parameters: detector: `DetectorType` that you want to execute + - Parameters: text: where we apply the regular expression + - Parameters: range: where we apply the regular expression + + - Returns: an array of `NSTextCheckingResult` that contains all maching for this detector or nil if it fails + */ + private func parseForMatches(with detector: DetectorType, in text: NSAttributedString, for range: NSRange) -> [NSTextCheckingResult] { + switch detector { + case .custom(let regex): + return regex.matches(in: text.string, options: [], range: range) + default: + fatalError("You must pass a .custom DetectorType") + } + } + private func setRangesForDetectors(in checkingResults: [NSTextCheckingResult]) { guard checkingResults.isEmpty == false else { return } @@ -354,7 +404,13 @@ open class MessageLabel: UILabel { let tuple: (NSRange, MessageTextCheckingType) = (result.range, .transitInfoComponents(result.components)) ranges.append(tuple) rangesForDetectors.updateValue(ranges, forKey: .transitInformation) - + case .regularExpression: + guard let text = text, let regex = result.regularExpression, let range = Range(result.range, in: text) else { return } + let detector = DetectorType.custom(regex: regex) + var ranges = rangesForDetectors[detector] ?? [] + let tuple: (NSRange, MessageTextCheckingType) = (result.range, .custom(pattern: regex.pattern, match: String(text[range]))) + ranges.append(tuple) + rangesForDetectors.updateValue(ranges, forKey: detector) default: fatalError("Received an unrecognized NSTextCheckingResult.CheckingType") } @@ -428,6 +484,16 @@ open class MessageLabel: UILabel { transformedTransitInformation[key.rawValue] = value } handleTransitInformation(transformedTransitInformation) + case let .custom(pattern, match): + guard let match = match else { return } + switch detectorType { + case .hashtag: + handleHashtag(match) + case .mention: + handleHashtag(match) + default: + handleCustom(pattern, match: match) + } } } @@ -450,13 +516,26 @@ open class MessageLabel: UILabel { private func handleTransitInformation(_ components: [String: String]) { delegate?.didSelectTransitInformation(components) } - + + private func handleHashtag(_ hashtag: String) { + delegate?.didSelectHashtag(hashtag) + } + + private func handleMention(_ mention: String) { + delegate?.didSelectMention(mention) + } + + private func handleCustom(_ pattern: String, match: String) { + delegate?.didSelectCustom(pattern, match: match) + } + } -private enum MessageTextCheckingType { +internal enum MessageTextCheckingType { case addressComponents([NSTextCheckingKey: String]?) case date(Date?) case phoneNumber(String?) case link(URL?) case transitInfoComponents([NSTextCheckingKey: String]?) + case custom(pattern: String, match: String?) } diff --git a/Tests/ControllersTest/MessageLabelSpec.swift b/Tests/ControllersTest/MessageLabelSpec.swift index abf28a00..5d62e52f 100644 --- a/Tests/ControllersTest/MessageLabelSpec.swift +++ b/Tests/ControllersTest/MessageLabelSpec.swift @@ -38,7 +38,90 @@ final class MessageLabelSpec: QuickSpec { messageLabel = MessageLabel() } -// describe("text recognized by a DetectorType") { + describe("text recognized by a DetectorType") { + + let mentionsList = ["@julienkode", "@facebook", "@google", "@1234"] + let hashtagsList = ["#julienkode", "#facebook", "#google", "#1234"] + + var detector: DetectorType! + var key: NSAttributedString.Key! + var attributes: [NSAttributedString.Key: Any]! + + context("Mention detection") { + + beforeEach { + detector = DetectorType.mention + key = NSAttributedString.Key(rawValue: "Mention") + attributes = [key: "MentionDetected"] + } + + it("match with multiples alpha and numerics") { + let text = mentionsList.joined(separator: " #test ") + self.set(text: text, and: [detector], with: attributes, to: messageLabel) + let matches = self.extractCustomDetectors(for: detector, with: messageLabel) + expect(matches).to(equal(mentionsList)) + } + + it("is invalid") { + let invalids = hashtagsList.joined(separator: " ") + self.set(text: invalids, and: [detector], with: attributes, to: messageLabel) + let matches = self.extractCustomDetectors(for: detector, with: messageLabel) + expect(matches.count).to(equal(0)) + } + + } + + context("Hashtag detection") { + + beforeEach { + detector = DetectorType.hashtag + key = NSAttributedString.Key(rawValue: "Hashtag") + attributes = [key: "HashtagDetected"] + } + + it("match with multiples alpha and numerics") { + let text = hashtagsList.joined(separator: " @test ") + self.set(text: text, and: [detector], with: attributes, to: messageLabel) + let matches = self.extractCustomDetectors(for: detector, with: messageLabel) + expect(matches).to(equal(hashtagsList)) + } + + it("is invalid") { + let invalids = mentionsList.joined(separator: " ") + self.set(text: invalids, and: [detector], with: attributes, to: messageLabel) + let matches = self.extractCustomDetectors(for: detector, with: messageLabel) + expect(matches.count).to(equal(0)) + } + + } + + context("Custom detection") { + + let shouldPass = ["1234", "1", "09876"] + let shouldFailed = ["abcd", "a", "!!!", ";"] + + beforeEach { + detector = DetectorType.custom(regex: try! NSRegularExpression(pattern: "[0-9]+", options: .caseInsensitive)) + key = NSAttributedString.Key(rawValue: "Custom") + attributes = [key: "CustomDetected"] + } + + it("must match with one or more numerics") { + let text = shouldPass.joined(separator: " ") + self.set(text: text, and: [detector], with: attributes, to: messageLabel) + let matches = self.extractCustomDetectors(for: detector, with: messageLabel) + expect(matches).to(equal(shouldPass)) + } + + it("must failed with any non numerics characters") { + let invalids = shouldFailed.joined(separator: " ") + self.set(text: invalids, and: [detector], with: attributes, to: messageLabel) + let matches = self.extractCustomDetectors(for: detector, with: messageLabel) + expect(matches.count).to(equal(0)) + } + + } + // context("address detection is enabled") { // it("applies addressAttributes to text") { // let expectedColor = UIColor.blue @@ -83,7 +166,7 @@ final class MessageLabelSpec: QuickSpec { // expect(textFont).to(equal(expectedFont)) // } // } -// } + } describe("the synchronization between text and attributedText") { context("when attributedText is set to a non-nil value") { @@ -237,6 +320,44 @@ final class MessageLabelSpec: QuickSpec { } } } + + // MARK: - Private helpers API for Detectors + + /** + Takes a given `DetectorType` and extract matches from a `MessageLabel` + + - Parameters: detector: `DetectorType` that you want to extract + - Parameters: label: `MessageLabel` where you want to get matches + + - Returns: an array of `String` that contains all matches for the given detector + */ + private func extractCustomDetectors(for detector: DetectorType, with label: MessageLabel) -> [String] { + guard let detection = label.rangesForDetectors[detector] else { return [] } + return detection.compactMap ({ (range, messageChecking) -> String? in + switch messageChecking { + case .custom(_, let match): + return match + default: + return nil + } + }) + } + + /** + Simply set text, detectors and attriutes to a given label + + - Parameters: text: `String` that will be applied to the label + - Parameters: detector: `DetectorType` that you want to apply to the label + - Parameters: attributes: `[NSAttributedString.Key: Any]` that you want to apply to the label + - Parameters: label: `MessageLabel` that takes the previous parameters + + */ + private func set(text: String, and detectors: [DetectorType], with attributes: [NSAttributedString.Key: Any], to label: MessageLabel) { + label.mentionAttributes = attributes + label.enabledDetectors = detectors + label.text = text + } + } // MARK: - Helpers From 8fb0600c12d6f8926301df269635d6ce06305564 Mon Sep 17 00:00:00 2001 From: Julien K Date: Wed, 31 Oct 2018 20:40:23 +0100 Subject: [PATCH 2/5] Fix handleMention call --- Sources/Views/MessageLabel.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/Views/MessageLabel.swift b/Sources/Views/MessageLabel.swift index d72a0ffc..7b51e342 100644 --- a/Sources/Views/MessageLabel.swift +++ b/Sources/Views/MessageLabel.swift @@ -490,7 +490,7 @@ open class MessageLabel: UILabel { case .hashtag: handleHashtag(match) case .mention: - handleHashtag(match) + handleMention(match) default: handleCustom(pattern, match: match) } From 9a64ebf6216243897af334b10e28d2d5b95e72a7 Mon Sep 17 00:00:00 2001 From: Julien K Date: Thu, 1 Nov 2018 11:25:45 +0100 Subject: [PATCH 3/5] [Examples] Add delegate methods --- .../View Controllers/ChatViewController.swift | 14 +++++++++++++- .../View Controllers/SettingsViewController.swift | 2 +- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/Example/Sources/View Controllers/ChatViewController.swift b/Example/Sources/View Controllers/ChatViewController.swift index a8de9166..412d796a 100644 --- a/Example/Sources/View Controllers/ChatViewController.swift +++ b/Example/Sources/View Controllers/ChatViewController.swift @@ -223,7 +223,19 @@ extension ChatViewController: MessageLabelDelegate { func didSelectTransitInformation(_ transitInformation: [String: String]) { print("TransitInformation Selected: \(transitInformation)") } - + + func didSelectHashtag(_ hashtag: String) { + print("Hashtag selected: \(hashtag)") + } + + func didSelectMention(_ mention: String) { + print("Mention selected: \(mention)") + } + + func didSelectCustom(_ pattern: String, match: String?) { + print("Custom data detector patter selected: \(pattern)") + } + } // MARK: - MessageInputBarDelegate diff --git a/Example/Sources/View Controllers/SettingsViewController.swift b/Example/Sources/View Controllers/SettingsViewController.swift index 882e0f9e..3db827a2 100644 --- a/Example/Sources/View Controllers/SettingsViewController.swift +++ b/Example/Sources/View Controllers/SettingsViewController.swift @@ -34,7 +34,7 @@ final internal class SettingsViewController: UITableViewController { return .lightContent } - let cells = ["Mock messages count", "Text Messages", "AttributedText Messages", "Photo Messages", "Video Messages", "Emoji Messages", "Location Messages", "Url Messages", "Phone Messages"] + let cells = ["Mock messages count", "Text Messages", "AttributedText Messages", "Photo Messages", "Video Messages", "Emoji Messages", "Location Messages", "Url Messages", "Phone Messages", "Hashtag Messages", "Mention Messages"] // MARK: - Picker From d238e09155f9347c601dcdaaf4a0cc25f468510b Mon Sep 17 00:00:00 2001 From: Julien K Date: Fri, 2 Nov 2018 11:17:39 +0100 Subject: [PATCH 4/5] [Update] --- Sources/Models/DetectorType.swift | 6 +++--- Sources/Views/MessageLabel.swift | 13 ++----------- Tests/ControllersTest/MessageLabelSpec.swift | 2 +- 3 files changed, 6 insertions(+), 15 deletions(-) diff --git a/Sources/Models/DetectorType.swift b/Sources/Models/DetectorType.swift index d3d43f2e..0842f41e 100644 --- a/Sources/Models/DetectorType.swift +++ b/Sources/Models/DetectorType.swift @@ -31,11 +31,11 @@ public enum DetectorType: Hashable { case phoneNumber case url case transitInformation - case custom(regex: NSRegularExpression) + case custom(NSRegularExpression) // swiftlint:disable force_try - public static var hashtag = DetectorType.custom(regex: try! NSRegularExpression(pattern: "#[a-zA-Z0-9]{4,}", options: [])) - public static var mention = DetectorType.custom(regex: try! NSRegularExpression(pattern: "@[a-zA-Z0-9]{4,}", options: [])) + public static var hashtag = DetectorType.custom(try! NSRegularExpression(pattern: "#[a-zA-Z0-9]{4,}", options: [])) + public static var mention = DetectorType.custom(try! NSRegularExpression(pattern: "@[a-zA-Z0-9]{4,}", options: [])) // swiftlint:enable force_try internal var textCheckingType: NSTextCheckingResult.CheckingType { diff --git a/Sources/Views/MessageLabel.swift b/Sources/Views/MessageLabel.swift index 402329b7..49664044 100644 --- a/Sources/Views/MessageLabel.swift +++ b/Sources/Views/MessageLabel.swift @@ -332,7 +332,7 @@ open class MessageLabel: UILabel { private func parse(text: NSAttributedString) -> [NSTextCheckingResult] { guard enabledDetectors.isEmpty == false else { return [] } let range = NSRange(location: 0, length: text.length) - var matches = [NSTextCheckingResult]() + var matches = [NSTextCheckingResult]() // Get matches of all .custom DetectorType and add it to matches array let regexs = enabledDetectors @@ -366,15 +366,6 @@ open class MessageLabel: UILabel { return results } - /** - Take a custom detector and apply the matching to the text - - - Parameters: detector: `DetectorType` that you want to execute - - Parameters: text: where we apply the regular expression - - Parameters: range: where we apply the regular expression - - - Returns: an array of `NSTextCheckingResult` that contains all maching for this detector or nil if it fails - */ private func parseForMatches(with detector: DetectorType, in text: NSAttributedString, for range: NSRange) -> [NSTextCheckingResult] { switch detector { case .custom(let regex): @@ -418,7 +409,7 @@ open class MessageLabel: UILabel { rangesForDetectors.updateValue(ranges, forKey: .transitInformation) case .regularExpression: guard let text = text, let regex = result.regularExpression, let range = Range(result.range, in: text) else { return } - let detector = DetectorType.custom(regex: regex) + let detector = DetectorType.custom(regex) var ranges = rangesForDetectors[detector] ?? [] let tuple: (NSRange, MessageTextCheckingType) = (result.range, .custom(pattern: regex.pattern, match: String(text[range]))) ranges.append(tuple) diff --git a/Tests/ControllersTest/MessageLabelSpec.swift b/Tests/ControllersTest/MessageLabelSpec.swift index 5d62e52f..cd16342a 100644 --- a/Tests/ControllersTest/MessageLabelSpec.swift +++ b/Tests/ControllersTest/MessageLabelSpec.swift @@ -101,7 +101,7 @@ final class MessageLabelSpec: QuickSpec { let shouldFailed = ["abcd", "a", "!!!", ";"] beforeEach { - detector = DetectorType.custom(regex: try! NSRegularExpression(pattern: "[0-9]+", options: .caseInsensitive)) + detector = DetectorType.custom(try! NSRegularExpression(pattern: "[0-9]+", options: .caseInsensitive)) key = NSAttributedString.Key(rawValue: "Custom") attributes = [key: "CustomDetected"] } From d7df6fd5a59ed6b66e9c4967d8ac9c9214f629e6 Mon Sep 17 00:00:00 2001 From: Julien K Date: Fri, 2 Nov 2018 11:59:53 +0100 Subject: [PATCH 5/5] [Examples] Update MessageTypes enum --- .../Sources/Data Generation/SampleData.swift | 70 +++++++++++-------- 1 file changed, 41 insertions(+), 29 deletions(-) diff --git a/Example/Sources/Data Generation/SampleData.swift b/Example/Sources/Data Generation/SampleData.swift index 14b24ed4..90ad1ecf 100644 --- a/Example/Sources/Data Generation/SampleData.swift +++ b/Example/Sources/Data Generation/SampleData.swift @@ -31,26 +31,38 @@ final internal class SampleData { private init() {} - enum MessageTypes: UInt32, CaseIterable { - case Text = 0 - case AttributedText = 1 - case Photo = 2 - case Video = 3 - case Emoji = 4 - case Location = 5 - case Url = 6 - case Phone = 7 - case Hashtag = 8 - case Mention = 9 - case Custom = 10 + enum MessageTypes: String, CaseIterable { + case text = "Text" + case attributedText = "AttributedText" + case photo = "Photo" + case video = "Video" + case emoji = "Emoji" + case location = "Location" + case url = "Url" + case phone = "Phone" + case hashtag = "Hashtag" + case mention = "Mention" + case custom = "Custom" + + static let all: [MessageTypes] = [ + .text, + .attributedText, + .photo, + .video, + .emoji, + .location, + .url, + .phone, + .hashtag, + .mention, + .custom + ] static func random() -> MessageTypes { - // Update as new enumerations are added - let maxValue = Custom.rawValue - - let rand = arc4random_uniform(maxValue+1) - return MessageTypes(rawValue: rand)! + let randomIndex = Int(arc4random()) % MessageTypes.all.count + return all[randomIndex] } + } let system = Sender(id: "000000", displayName: "System") @@ -140,7 +152,7 @@ final internal class SampleData { func randomMessageType() -> MessageTypes { let messageType = MessageTypes.random() - if !UserDefaults.standard.bool(forKey: "\(messageType)" + " Messages") { + if !UserDefaults.standard.bool(forKey: "\(messageType.rawValue)" + " Messages") { return randomMessageType() } @@ -156,36 +168,36 @@ final internal class SampleData { let date = dateAddingRandomTime() switch randomMessageType() { - case .Text: + case .text: let randomSentence = Lorem.sentence() return MockMessage(text: randomSentence, sender: sender, messageId: uniqueID, date: date) - case .AttributedText: + case .attributedText: let randomSentence = Lorem.sentence() let attributedText = attributedString(with: randomSentence) return MockMessage(attributedText: attributedText, sender: senders[randomNumberSender], messageId: uniqueID, date: date) - case .Photo: + case .photo: let randomNumberImage = Int(arc4random_uniform(UInt32(messageImages.count))) let image = messageImages[randomNumberImage] return MockMessage(image: image, sender: sender, messageId: uniqueID, date: date) - case .Video: + case .video: let randomNumberImage = Int(arc4random_uniform(UInt32(messageImages.count))) let image = messageImages[randomNumberImage] return MockMessage(thumbnail: image, sender: sender, messageId: uniqueID, date: date) - case .Emoji: + case .emoji: let randomNumberEmoji = Int(arc4random_uniform(UInt32(emojis.count))) return MockMessage(emoji: emojis[randomNumberEmoji], sender: sender, messageId: uniqueID, date: date) - case .Location: + case .location: let randomNumberLocation = Int(arc4random_uniform(UInt32(locations.count))) return MockMessage(location: locations[randomNumberLocation], sender: sender, messageId: uniqueID, date: date) - case .Url: + case .url: return MockMessage(text: "https://github.com/MessageKit", sender: sender, messageId: uniqueID, date: date) - case .Phone: + case .phone: return MockMessage(text: "123-456-7890", sender: sender, messageId: uniqueID, date: date) - case .Mention: + case .mention: return MockMessage(text: "@messagekit", sender: sender, messageId: uniqueID, date: date) - case .Hashtag: + case .hashtag: return MockMessage(text: "#messagekit", sender: sender, messageId: uniqueID, date: date) - case .Custom: + case .custom: return MockMessage(custom: "Someone left the conversation", sender: system, messageId: uniqueID, date: date) } }