Crash in string[range]

I made:

let substring = string[range]

and got:

Fatal error: String index is out of bounds

So I checked:

startIndex ≤ lowerBound ≤ upperBound ≤ endIndex

All ok, but still same crash. So I did:

let stringLimits = string.startIndex ..< string.endIndex 
let clampedRange = range.clamped(to: stringLimits) 
let substring = string[clampedRange]

Same crash.

Is there a function like:

Bool ok = string(compatibleWith: range)

Or something to this effect?

The reason seems to be that my range is not relative to my string.

On macOS:

@State var texSelection: TextSelection? = nil
...
Text("Click here")	 
.textSelection(.enabled)
.onTapGesture	//	does NOT work on macOS
{
	print("onTapGesture in Text")
}	

TextField("Enter Text", text: $string, selection: $texSelection)

Clicking in the TextField sets texSelection; so far ok.

But clicking in Text also sets texSelection with a range relative to "Click here". This then will result further down in: "Fatal error: String index is out of bounds"

Any ideas what to do?

macOS 15.2
Xcode Version 16.2 (16C5032a)

This interaction between textSelection() and a @State ... : TextSelection where all selected text in the View uses the same State variable is, on first usage, jarring to say the least. This is largely a SwiftUI framework quirk.

However, to help with the immediate issue, you can use FocusState to determine whether the TextField or any Text_ views are being focused and only get the substring from that relevant text source.

Given your little snippet at the end, here is a sample that will get the relevant selected text either the Text or TextField.

struct ContentView: View {
    
    @State
    var textSelection: TextSelection? = nil
    @State
    var string: String = "asdf"
    @State
    var textText: String = "Click here"
    
    @FocusState
    private var isTextFieldFocused: Bool
    
    var body: some View {
        VStack {
            Text(textText)
                .textSelection(.enabled)
            
            TextField(
                "Enter Text",
                text: $string,
                selection: $textSelection
            )
            .focused($isTextFieldFocused)
        }
        .onChange(of: textSelection) { _, newValue in
            guard let newValue else { return }
            
            switch newValue.indices {
            case .selection(let range):
                if isTextFieldFocused {
                    print("TextField selection: \(string[range])")
                } else {
                    print("Text selection: \(textText[range])")
                }
            case .multiSelection(let rangeSet):
                if isTextFieldFocused {
                    print("TextField selection: \(string[rangeSet])")
                } else {
                    print("Text selection: \(textText[rangeSet])")
                }
            }
        }
    }
}

#Preview {
    ContentView()
}

Thanks a lot!

But:
click on TextField
then click on Text
→ crash

To fix this, change:
.onChange(of: textSelection)
{ _, newValue in
...
to:
.onChange(of: textSelection)
{
let newValue = textSelection

Gerriet.