Michel Koga
Michel Koga

Reputation: 49

NSViewRepresentable is crashing Binding to SwiftData?

I am trying to create Markdown in TextEditor using NSViewRepresentable, it is working fine, but the binding from NSViewRepresentable seems to confuse SwiftData. It will update properly until I select one item on the list, after that everything I write will be updated on the first item I clicked, even after selecting other item on the list. This won't happen if I link to default TextEditor view. Am I doing something wrong, or it's a bug?

Here is the "Content View":

struct ContentView: View {
    @Environment(\.modelContext) private var modelContext
    @Query(sort: \Entry.timestamp, order: .reverse, animation: .default) private var entries: [Entry] 
    
    @State var selection: Entry? = nil 
    
    var body: some View {
        NavigationSplitView {
            List(selection: $selection) {
                ForEach(entries) { entry in
                    @Bindable var bindableEntry = entry
                    NavigationLink {
                        RichTextEditor(text: $bindableEntry.text)
                    } label: {
                        ListItemView(entry: entry, isFavorite: $bindableEntry.isFavorite)
                            .keyboardShortcut(.delete, modifiers: [])
                            .onDeleteCommand(perform: {
                                delete(entry)
                            })....
}

And here is the NSViewRepresentable:


struct RichTextEditor: NSViewRepresentable {
    @Binding var text: String
    
    init(text: Binding<String>) {
        _text = text
    }
    
    func makeNSView(context: Context) -> NSScrollView {
        let scrollView = MyTextView.scrollableTextView()
        let textView = scrollView.documentView as! MyTextView
        
        textView.delegate = context.coordinator
        
        return scrollView
    }
    
    func updateNSView(_ nsView: NSScrollView, context: Context) {
        let textView = nsView.documentView as! MyTextView
        let selectedRanges = textView.selectedRanges
        textView.string = text
        textView.selectedRanges = selectedRanges
    }
    func makeCoordinator() -> Coordinator {
        return Coordinator(text: $text)
    }
    class Coordinator: NSObject, NSTextViewDelegate {
        @Binding var text: String
        init(text: Binding<String>) {
            _text = text
        }
        func textDidChange(_ notification: Notification) {
            if let textView = notification.object as? MyTextView {
               print("textDidChange")
                
                let selectedRanges = textView.selectedRanges
                text = textView.string
                
                textView.selectedRanges = selectedRanges
            }
        }
    }
}

It is wrapped on a NSScrollView because it NSTextView doesn't have scroll, but the same thing happens if I use a NSTextView.

I think is something related to the text = textView.string in "textDidChange" on "Coordinator", I think it's strange that I have to set the value of a binding property, but it won't save it in swift data if I don't do it and I don't know how I could make it different.

Upvotes: 1

Views: 192

Answers (0)

Related Questions