Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift 3: Getting attributes at substring in NSAttributedString

One of my controllers has an NSAttributeString which has a link in it:

@IBOutlet var textView: UITextView!

// Below is extracted from viewDidLoad()
let linkStr = "Click <a href='http://google.com'>here</a> for good times."
let attributedText = try! NSAttributedString(
  data: linkStr.data(using: String.Encoding.unicode, allowLossyConversion: true)!,
  options: [ NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType],
  documentAttributes: nil)
textView.attributedText = attributedText

I'm writing a unit test for the controller and I want to verify that the correct link was put on the "here" text. (The link is actually generated dynamically, which is why I want to test it).

Anyway, I can obviously get the unattributed text like this:

let text = viewController.textView.attributedText.string
// text == "Click here for good times."

I can also get the link attribute from "here" by doing something like this:

let url = uviewController.textView.attributedText.attribute(
    "NSLink", at: 6, effectiveRange: nil)
// url == A URL object for http://google.com.

The problem is I had to hardcode that "6" for the at parameter. The value of linkStr could change in the future and I don't want to have to update my test every time. For this case, we can assume it will always have the word "here" with the link attached to that word.

So what I'd like to do is find the character for where the word "here" is in linkStr and pass that value into the at parameter in order to pull out the NSLink attribute and verify it is pointing to the right URL. But I'm having trouble making heads or tails of how to use string ranges and indexes in Swift to pull this off.

Any suggestions?

like image 758
Chad Avatar asked Apr 13 '17 18:04

Chad


1 Answers

Here's how you can do it without hardcoding. This is Swift 3 playground code based on your sample:

import UIKit
import PlaygroundSupport

let linkStr = "Click <a href='http://google.com'>here</a> for good times."
let attributedText = try! NSAttributedString(
    data: linkStr.data(using: String.Encoding.unicode, allowLossyConversion: true)!,
    options: [ NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType],
    documentAttributes: nil)

attributedText.enumerateAttribute(NSAttributedString.Key.link, in: NSMakeRange(0, attributedText.length), options: [.longestEffectiveRangeNotRequired]) { value, range, isStop in
    if let value = value {
        print("\(value) found at \(range.location)")
    }
}

The print statement outputs:

http://google.com/ found at 6

p.s. 'NSAttributedString.Key.link' is instead of 'NSLinkAttributeName' because of the renaming.

like image 143
David S. Avatar answered Nov 01 '22 08:11

David S.