Search code examples
iosswiftstringnsstringnsattributedstring

Finding occurrences of String as NSRange from an NSString using Swift, result NSRange to be used in NSAttributedString


I am trying to find NSRange's of several strings within an string.

In my following code, I use String.range(of: String, options: , Range) method to find Range, and convert to NSRange. this conversion fails when text contains multi-code unit unicode characters, such as emoji:

let findInString = "This #is a #tag #tag inten#sive🔦#search" // MAY CONTAINS EMOJIS
let findStrings = ["#is","#tag","#sive","#search"]
let result = NSMutableAttributedString(string: findInString)

for (index, stringToFind) in findStrings.enumerated() {

    var nextStartIndex = findInString.startIndex

    while let range = findInString.range(of: stringToFind, options: [.literal, .caseInsensitive], range: nextStartIndex..<findInString.endIndex) {

        let start = findInString.distance(from: findInString.startIndex, to: range.lowerBound)
        let length = findInString.distance(from: range.lowerBound, to: range.upperBound)

        result.addAttribute(NSLinkAttributeName, value: "\(index):", range: NSMakeRange(start, length))

        nextStartIndex = range.upperBound
    }

}

Question: Will it work If I use NSString.range() to find NSRange. I am trying this, but my following code have an error in range: part.

let findInNsString = findInString as NSString
while let range = findInNsString.range(of: stringToFind, options: [.literal, .caseInsensitive], range: nextStartIndex..<findInString.endIndex)

I need help to understand and correct the above error, thanks in advance.


Solution

  • Found the correct way to convert a Range to NSRange, thanks to MartinR for this answer

    I was using the wrong way to convert Range to NSRange, here is the working code snippet with proper way to convert from Range to NSRange:

    let findStrings = ["#is","#tag","#siØve","#search"]
    let findInString = "This #is a #tag #tag inten#siØve🔦#search"
    let result = NSMutableAttributedString(string: findInString)
    let utf16 = findInString.utf16
    for (index, stringToFind) in findStrings.enumerated() {
    
        var nextStartIndex = findInString.startIndex
    
        while let range = findInString.range(of: stringToFind, options: [.literal, .caseInsensitive], range: nextStartIndex..<findInString.endIndex) {
    
            // PROPER WAY TO CONVERT TO NSRange
            let from = range.lowerBound.samePosition(in: utf16)
            let start = utf16.distance(from: utf16.startIndex, to: from)
            let length = utf16.distance(from: from, to: range.upperBound.samePosition(in: utf16))
    
            result.addAttribute(NSLinkAttributeName, value: "\(index):", range: NSMakeRange(start, length))
    
            nextStartIndex = range.upperBound
        }
    
    }