Convert a Swift Range to Use String.Index
The Situation
I was working on a SwiftUI app where I wanted to select text in a text view and insert some text at the start of the selection. SwiftUI’s native text editor currently does not support getting the selection range so I had to wrap an AppKit text view and store the selection range when the selection changes.
func textViewDidChangeSelection(_ notification: Notification) {
guard let textView = notification.object as?
NSTextView else {
return
}
control.model.selectionRange =
Range.init(textView.selectedRange())
}
The Problem
The code in the previous example gives me a Swift range, which uses integers, Range<Int>
. I wanted to use the selection range’s start index as the destination to insert the string.
model.text.insert(contentsOf: textToInsert,
at: model.selectionRange?.startIndex)
But the compiler gave me an error. The reason for the error is the String
struct’s insert
method takes a range that uses string indexes, Range<String.Index>
, not integers.
The Solution
The solution is to create a string index for the string and use that as the argument to the insert
method. The base for the string index is the string’s start index. The offset for the string index is the selection range’s lower bound.
let insertionPoint = model.text.index(model.text.startIndex,
offsetBy: model.selectionRange?.lowerBound ?? 0)
model.text.insert(contentsOf: textToInsert, at: insertionPoint)