9

I was trying to migrate my app to Swift 4, Xcode 9. I get this error. Its coming from a 3rd party framework.

distance(from:to:)' is unavailable: Any String view index conversion can fail in Swift 4; please unwrap the optional indices

func nsRange(from range: Range<String.Index>) -> NSRange {
    let utf16view = self.utf16
    let from = range.lowerBound.samePosition(in: utf16view)
    let to = range.upperBound.samePosition(in: utf16view)
    return NSMakeRange(utf16view.distance(from: utf16view.startIndex, to: from), // Error: distance(from:to:)' is unavailable: Any String view index conversion can fail in Swift 4; please unwrap the optional indices
                       utf16view.distance(from: from, to: to))// Error: distance(from:to:)' is unavailable: Any String view index conversion can fail in Swift 4; please unwrap the optional indices
}
E.Ergit
  • 111
  • 1
  • 8
  • https://stackoverflow.com/a/30404532/2083655 – Silmaril Sep 21 '17 at 09:09
  • 3
    Swift 4 has built-in methods to convert between String and NSString ranges, therefore you don't need that extension anymore. Have a look at the latest update to https://stackoverflow.com/a/30404532/1187415. – Martin R Sep 22 '17 at 05:23
  • really great, glad I saw your comment, almost missed :) thanks – AamirR Jan 22 '18 at 13:18

3 Answers3

13

You can simply unwrap the optional indices like this:

func nsRange(from range: Range<String.Index>) -> NSRange? {
    let utf16view = self.utf16
    if let from = range.lowerBound.samePosition(in: utf16view), let to = range.upperBound.samePosition(in: utf16view) {
       return NSMakeRange(utf16view.distance(from: utf16view.startIndex, to: from), utf16view.distance(from: from, to: to))
    }
    return nil
}
Alessandro Ornano
  • 34,887
  • 11
  • 106
  • 133
-1

The error says that the distances you are generating are optionals and need to be unwrapped. Try this:

func nsRange(from range: Range<String.Index>) -> NSRange {
    let utf16view = self.utf16
    guard let lowerBound = utf16view.distance(from: utf16view.startIndex, to: from), let upperBound = utf16view.distance(from: from, to: to) else { return NSMakeRange(0, 0) }
    return NSMakeRange(lowerBound, upperBound)
}

However the return could be handled better in the guard statement. I'd recommend making the return type of the function NSRange? and checking for nil wherever you call the function to avoid inaccurate values being returned.

Wes
  • 1,032
  • 7
  • 11
  • Its throwing error `Initializer for conditional binding must have Optional type, not 'String.UTF16View.IndexDistance' (aka 'Int')` – Vini App Sep 21 '17 at 19:18
-1

Please check :

let dogString = "Dog‼"
let range = dogString.range(of: "")!

// This is using Range

let strRange = dogString.range(range: range)
print((dogString as NSString).substring(with: strRange!)) // 

extension String {
    func range(range : Range<String.Index>) -> NSRange? {
        let utf16view = self.utf16
        guard
            let from = String.UTF16View.Index(range.lowerBound, within: utf16view),
            let to = String.UTF16View.Index(range.upperBound, within: utf16view)
        else { return nil }
        let utf16Offset = utf16view.startIndex.encodedOffset
        let toOffset = to.encodedOffset
        let fromOffset = from.encodedOffset
        return NSMakeRange(fromOffset - utf16Offset, toOffset - fromOffset)
    }
}

// This is using NSRange

let strNSRange = dogString.range(nsRange: NSRange(range, in: dogString))
print((dogString as NSString).substring(with: strNSRange!)) // 

extension String {
    func range(nsRange: NSRange) -> NSRange? {
        guard
            let from16 = utf16.index(utf16.startIndex, offsetBy: nsRange.location, limitedBy: utf16.endIndex),
            let to16 = utf16.index(utf16.startIndex, offsetBy: nsRange.length, limitedBy: utf16.endIndex),
            let from = from16.samePosition(in: self),
            let to = to16.samePosition(in: self)
            else { return nil }
        return NSMakeRange(from.encodedOffset, to.encodedOffset)
    }
}
Vini App
  • 7,339
  • 2
  • 26
  • 43