Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 0 additions & 27 deletions Example/Shared/Render/Async/AsyncRenderExample.swift

This file was deleted.

59 changes: 59 additions & 0 deletions Example/Shared/Render/Async/AsyncRendererExample.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
//
// AsyncRendererExample.swift
// Shared

#if OPENSWIFTUI
import OpenSwiftUI
#else
import SwiftUI
#endif

struct AsyncRendererExample: View {
@State private var items = [6]

var body: some View {
VStack(spacing: 10) {
ForEach(items, id: \.self) { item in
Color.blue.opacity(Double(item) / 6.0)
.frame(height: 50)
.transition(.slide)
}
}
.animation(.easeInOut(duration: 2), value: items)
.onAppear {
items.removeAll { $0 == 6 }
}
}
}

struct AsyncRendererTransitionExample: View {
@State private var isVisible: Bool = false

var body: some View {
Group {
if isVisible {
Color.red
.frame(width: 80, height: 80)
.clipShape(RoundedRectangle(cornerRadius: 0))
.transition(RenderCrashTransition())
}
}
.frame(width: 200, height: 200)
.animation(.linear(duration: 1), value: isVisible)
.onAppear {
guard !isVisible else {
return
}
isVisible = true
}
}

// This custom transition forces the async renderer to update inherited view
// content while a transition phase is changing. It covers the path that used
// to trip Swift's exclusivity checks in DisplayList.ViewUpdater.
private struct RenderCrashTransition: Transition {
func body(content: Content, phase: TransitionPhase) -> some View {
content.opacity(phase.isIdentity ? 1 : 0.1)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -859,8 +859,25 @@ extension NSHostingView {
}

public func _renderAsyncForTest(interval: Double) -> Bool {
_openSwiftUIUnimplementedWarning()
return false
advanceTimeForTest(interval: interval)
canAdvanceTimeAutomatically = false
var result = true
repeat {
RunLoop.flushObservers()
let didRender = Update.locked {
renderAsync(targetTimestamp: nil) != nil
}
if didRender {
CATransaction.flush()
if result {
result = !viewGraph.updateRequiredMainThread
}
} else {
result = false
}
} while !propertiesNeedingUpdate.isEmpty

@augmentcode augmentcode Bot Jun 10, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

repeat { ... } while !propertiesNeedingUpdate.isEmpty can potentially spin forever if renderAsync(targetTimestamp:) returns nil while propertiesNeedingUpdate stays non-empty (e.g., a stuck update flag), which would hang tests. Consider ensuring the loop has an exit condition when didRender is false (and that canAdvanceTimeAutomatically is always restored).

Severity: medium

Fix This in Augment

🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.

canAdvanceTimeAutomatically = true
return result
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -393,7 +393,7 @@ extension DisplayList {
newItem: newItem,
newState: newStatePtr,
tag: .inherited
) { layer, _, oldItem, oldState, newItem, newState in
) { [platform] layer, _, oldItem, oldState, newItem, newState in
platform.updateInheritedLayerAsync(
layer: &layer,
oldItem: oldItem,
Expand Down
Loading