Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Runtime Warning: Publishing from within view updates #56

Open
Smail opened this issue Mar 2, 2025 · 1 comment
Open

Runtime Warning: Publishing from within view updates #56

Smail opened this issue Mar 2, 2025 · 1 comment

Comments

@Smail
Copy link

Smail commented Mar 2, 2025

I am receiving multiple "Publishing changes from within view updates is not allowed, this will cause undefined behavior" warning in iOS 18.3.1. This happens for example when a LaTeX view is conditionally displayed, e.g.:

@State private var isDisplayed = false

var body: some View {
    if isDisplayed {
        LaTeX("Hello World")
    }
    Button("Click Me") { isDisplayed.toggle() }
}

The issue is the renderSync function, specifically where variables are set:

@MainActor func renderSync(
    latex: String,
    unencodeHTML: Bool,
    parsingMode: LaTeX.ParsingMode,
    processEscapes: Bool,
    errorMode: LaTeX.ErrorMode,
    font: Font,
    displayScale: CGFloat,
    renderingMode: SwiftUI.Image.TemplateRenderingMode
  ) -> [ComponentBlock] {
  guard !isRendering else {
    return []
  }
  guard !rendered else {
    return blocks
  }
  isRendering = true // <-- HERE
  
  let texOptions = TeXInputProcessorOptions(processEscapes: processEscapes, errorMode: errorMode)
  let renderedBlocks = render(
    blocks: parseBlocks(latex: latex, unencodeHTML: unencodeHTML, parsingMode: parsingMode),
    font: font,
    displayScale: displayScale,
    renderingMode: renderingMode,
    texOptions: texOptions)
  
  blocks = renderedBlocks  // <-- HERE
  isRendering = false      // <-- HERE
  rendered = true          // <-- HERE
  return blocks
}

I found a solution, but I am not sure if it captures the essence of the renderSync function:

@MainActor func renderSync(
  latex: String,
  unencodeHTML: Bool,
  parsingMode: LaTeX.ParsingMode,
  processEscapes: Bool,
  errorMode: LaTeX.ErrorMode,
  font: Font,
  displayScale: CGFloat,
  renderingMode: SwiftUI.Image.TemplateRenderingMode
) -> [ComponentBlock] {
  guard !isRendering else {
    return []
  }
  guard !rendered else {
    return blocks
  }
  
  let texOptions = TeXInputProcessorOptions(processEscapes: processEscapes, errorMode: errorMode)
  let renderedBlocks = render(
    blocks: parseBlocks(latex: latex, unencodeHTML: unencodeHTML, parsingMode: parsingMode),
    font: font,
    displayScale: displayScale,
    renderingMode: renderingMode,
    texOptions: texOptions)
  
  // Schedule updates for the next view cycle on the main actor outside the current view update
  Task {
    await MainActor.run {
      isRendering = true
      blocks = renderedBlocks
      isRendering = false
      rendered = true
    }
  }
  
  return renderedBlocks
}

Now, it still computes and returns the new renderedBlocks synchronously, but they are only registered for the next view cycle in the UI (I believe).
I tried the .wait rendering method with my approach, and it still seems to block the UI, just like the first version did. But I am obviously unsure if this is the way to go and if I should create a PR.

@ddwty
Copy link

ddwty commented Mar 9, 2025

I'm facing the same issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants