From 9405e201bc84765a0cfe15952a551ea86f217ec2 Mon Sep 17 00:00:00 2001 From: Dark-detsixE Date: Sun, 23 Aug 2026 15:31:50 +0800 Subject: [PATCH 1/2] Fix ContentPathObservers.notifyPathChanged --- .../OpenSwiftUICore/Event/Responder/LeafViewResponder.swift | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Sources/OpenSwiftUICore/Event/Responder/LeafViewResponder.swift b/Sources/OpenSwiftUICore/Event/Responder/LeafViewResponder.swift index e8cf64015..7c82b2e95 100644 --- a/Sources/OpenSwiftUICore/Event/Responder/LeafViewResponder.swift +++ b/Sources/OpenSwiftUICore/Event/Responder/LeafViewResponder.swift @@ -32,13 +32,12 @@ struct ContentPathObservers { mutating func notifyPathChanged(for parent: ViewResponder, changes: ContentPathChanges, transform: (old: ViewTransform, new: ViewTransform)) { let oldObservers = observers observers = [] - var failedObservers: [Observer] = [] for observer in oldObservers { var result = true guard let value = observer.value else { continue } value.contentPathDidChange(for: parent, changes: changes, transform: transform, finished: &result) guard !result else { continue } - failedObservers.append(observer) + observers.append(observer) } } } From 675f81547efad9ed17f1a74a3070f695fa345e66 Mon Sep 17 00:00:00 2001 From: Kyle Date: Sun, 23 Aug 2026 23:11:56 +0800 Subject: [PATCH 2/2] Add ContentPathObservers regression test --- .../Event/ContentPathObserverTests.swift | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 Tests/OpenSwiftUICoreTests/Event/ContentPathObserverTests.swift diff --git a/Tests/OpenSwiftUICoreTests/Event/ContentPathObserverTests.swift b/Tests/OpenSwiftUICoreTests/Event/ContentPathObserverTests.swift new file mode 100644 index 000000000..7ca892e79 --- /dev/null +++ b/Tests/OpenSwiftUICoreTests/Event/ContentPathObserverTests.swift @@ -0,0 +1,46 @@ +// +// ContentPathObserverTests.swift +// OpenSwiftUICoreTests + +@_spi(ForOpenSwiftUIOnly) import OpenSwiftUICore +@testable import OpenSwiftUICore +import Testing + +struct ContentPathObserverTests { + @Test + func unfinishedObserverIsRetainedUntilFinished() { + var observers = ContentPathObservers() + let observer = TestContentPathObserver() + // The test observer does not inspect its parent. Avoid constructing a + // ViewResponder, whose public initializer requires a live ViewGraph. + let parentObject: AnyObject = ResponderNode() + let parent = unsafeBitCast(parentObject, to: ViewResponder.self) + let transform = (old: ViewTransform(), new: ViewTransform()) + + observers.addObserver(observer) + observers.notifyPathChanged(for: parent, changes: [], transform: transform) + #expect(observer.callCount == 1) + + observers.notifyPathChanged(for: parent, changes: [], transform: transform) + #expect(observer.callCount == 2) + + observers.notifyPathChanged(for: parent, changes: [], transform: transform) + #expect(observer.callCount == 2) + } +} + +private final class TestContentPathObserver: ContentPathObserver { + private(set) var callCount = 0 + + func respondersDidChange(for parent: ViewResponder) {} + + func contentPathDidChange( + for parent: ViewResponder, + changes: ContentPathChanges, + transform: (old: ViewTransform, new: ViewTransform), + finished: inout Bool + ) { + callCount += 1 + finished = callCount >= 2 + } +}