danielsaidi / RichTextKit

RichTextKit is a Swift SDK that helps you use rich text in Swift and SwiftUI.
MIT License
885 stars 114 forks source link

crash calling RichTextContext.setAttributedString from Task() #172

Closed S1D1T1 closed 5 months ago

S1D1T1 commented 5 months ago

Don't know if this is my bug or yours, but I have a workaround:

wishing to maintain 1 source of truth, my object has a computed string value that reaches down into the attributed string. I believe I'm permitted to get/set. (all code samples simplified from the real app.)

class PromptWindowInfo {
...
    var posContext = RichTextContext()

    var posPrompt:String {
        get {
          return posContext.plainText() // my wrapper fn
        }
        set {
          posContext.setAttributedString(to: newValue)
        }
    }
}

I get/set this string value at numerous points. However, within a Task context, setting the value crashes in RichTextView_AppKit.swift, line 199

    Task{
            await SomeServerRequest(serverParams)
        let newPrompt = serverParams.newPrompt
        windowInfo.posPrompt = newPrompt    // <- crash
      }

Solution: defer the setting of the attributed string with

    DispatchQueue.main.async {
     windowInfo.posPrompt = newPrompt
     }

This is the only place where I've needed to do that.

The assertion fail happens below in RichTextContext_AppKit.swift

public extension RichTextView {

    /// Get the rich text that is managed by the view.
    var attributedString: NSAttributedString {
        get { attributedString() }
        set { textStorage?.setAttributedString(newValue) }  //  <<CRASH HERE
    }

    /// Whether or not the text view is the first responder.
    var isFirstResponder: Bool {
        window?.firstResponder == self
    }
}
danielsaidi commented 5 months ago

Hi @S1D1T1

When you run the async operation, there's no guarantee that the line after will be executed on the main thread. Thus, you must pop back to the main thread to keep working with the UI.

S1D1T1 commented 5 months ago

thanks for confirming. the world of async, wait, task, etal is not my comfort zone.