SwiftUI TextView Coordinator bindings retain first object

I am trying to create a TextView to use with SwiftUI and have use the code shown below to create the TextView.

This all seems to work fine except that the TextView retains the binding to the first object for all the updates.

For example if the TextView is used I a Master Detail arrangement then it will always update the first object that was selected. It seems the binding does not update to subsequent objects.

I have created a small sample application you can test out here. Run the app and select one of the objects in the left panel list and then try editing the textView and the textField. The text fields work as expected but the textView does not.

https://duncangroenewald.com/files/SampleApps/TextView.zip


import SwiftUI



// OSTextView is a subclass of NSTextView - but just use NSTextView if required



#if !os(macOS)

 

struct TextView: UIViewRepresentable {

 

    @Binding var attributedText: NSAttributedString

    

    func makeUIView(context: Context) -> OSTextView {

        let textView = OSTextView()

        textView.delegate = context.coordinator

 

        return textView

    }

 

    func updateUIView(_ uiView: OSTextView, context: Context) {

        uiView.attributedText = attributedText

    }

    

    func makeCoordinator() -> Coordinator {

        Coordinator($attributedText)

    }

     

    class Coordinator: NSObject, UITextViewDelegate {

        var text: Binding<NSAttributedString>

     

        init(_ text: Binding<NSAttributedString>) {

            self.text = text

        }

     

        func textViewDidChange(_ textView: UITextView) {

            self.text.wrappedValue = textView.attributedText

        }

    }

}



#endif



#if os(macOS)

 

struct TextView: NSViewRepresentable {

 

    @Binding var attributedText: NSAttributedString

    

    func makeNSView(context: Context) -> OSTextView {

        let textView = OSTextView(frame: .zero)

        textView.delegate = context.coordinator

        return textView

    }

 

    func updateNSView(_ nsView: OSTextView, context: Context) {

        nsView.textStorage?.setAttributedString(attributedText)

    }

    

    func makeCoordinator() -> Coordinator {

        return Coordinator($attributedText)

    }

     

    class Coordinator: NSObject, NSTextViewDelegate {

        var text: Binding<NSAttributedString>

     

        init(_ text: Binding<NSAttributedString>) {

            self.text = text

            super.init()

        }

     

        func textDidChange(_ notification: Notification) {

            if let textView = notification.object as? NSTextView {

                self.text.wrappedValue = textView.attributedString()

            }

        }

    }

}



#endif

Replies

class Doc: NSObject, ObservableObject, Identifiable {

    @State private(set) var id: String

    @State var name: String

    @State var details: NSAttributedString

    // Used to demonstrate correct bindings with TextField

    var details2: String

    init(name: String, details: NSAttributedString) {

        self.id = UUID().uuidString

        self.name = name

        self.details = details

        self.details2 = details.string

    }

}
struct ContentView: View {

    @State var selection: Doc?

    var body: some View {
        HStack {
            List(docs) { doc in
                Text(doc.name).onTapGesture {
                    selection = nil
                    selection = doc
                }.background((selection == doc) ? Color.gray : Color.clear)
            }