7

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?

1 Answer 1

7

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.

Sign up to request clarification or add additional context in comments.

1 Comment

Wow... never would've guessed that. Swift is... interesting sometimes. Thanks!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.