Refresh the FlowLink tap closure in updateUIView - #52
Open
TaylorAanenson wants to merge 1 commit into
Open
Conversation
GestureContainer's coordinator is created once and reused, and updateUIView was empty, so the UIButton kept invoking the first onTap it was ever handed. That closure captured the FlowLink struct from its first body evaluation — including `value` and `label` — so a link whose content is refreshed in place kept pushing the value it had at first render and snapshotting the label it had at first render, however many times SwiftUI re-rendered it. Two visible symptoms in an app that renders a disk cache and then swaps in a fetched payload under the same identity: taps open the cached copy of a row for the rest of the session, and the transition snapshot shows numbers the card no longer displays. retakeSnapshots can't help with the latter — it faithfully re-renders the stale label.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The problem
GestureContainer.makeCoordinator()storesonTapon the coordinator, andupdateUIViewis empty. SwiftUI creates a coordinator once per representable and reuses it across updates, so theUIButtonkeeps invoking the very firstonTapit was handed.That closure is
{ trigger() }, capturing theFlowLinkstruct from its first body evaluation — including itsvalueand itslabel. Every later body pass builds a new struct that the button never sees.Symptoms
For a
FlowLinkwhose row is refreshed in place — same identity, new content — two things go wrong and neither is fixable from the call site:trigger()appends the first-rendervalue, so the destination receives outdated data. This bites hard in the common pattern of rendering a cached list and then swapping in a fetched payload under the sameid: taps open the cached copy of a row for the rest of the session.initSnapshots()renders the first-renderlabel, so the image painted over the zoom shows content the row no longer displays.retakeSnapshots: truedoesn't help — it faithfully re-renders the stale label.Repro
A list whose rows come from a cache and are then replaced by fetched data under the same identity. Render it, let the fetch land so a row visibly updates, then tap that row — the destination gets the pre-fetch value. Deleting the cache so the row's first render is the fetched payload makes the same tap behave correctly.
The fix
One line, matching the standard
UIViewRepresentablecontract of pushing current state inupdateUIView. TheisPressedbinding is left alone: it targets a stable@Statebox, so it doesn't go stale the way the escaping closure does.