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.
Change summary
This fixes a tricky sizing issue that occurs due to slightly incorrect timing when setting up an
EpoxySwiftUIHostingView
for use in a collection view cell. The issue occurs due to us not installing our hosting controller (view controller) as a child prior to the cell being asked to size itself. The end result is that the cell sizes itself without having the hosting controller completely installed, and therefore lays out with its estimated size.In most "normal" cases, there's no timing or sizing issue. However, an edge case exists where we update our collection view while it's currently off screen (for example, when it's not in the selected tab / view controller of a tab controller). Here's what that bug looks like in the Airbnb app:
RPReplay_Final1692239266.MP4
I spent some time creating a small project to reproduce the issue, and eventually identified exactly what Epoxy was doing differently compared to my own hosting controller / cell solution. Here's the bug reproduced in the example project:
The bug
The root issue was that we were using
didMoveToWindow
/ the presence of a window as an indicator of whether or not we'd be able to find a prospective parent view controller up our responder chain - we need this view controller to install our hosting controller as a child. The assumption that we need a non-nilwindow
in order to find a parent view controller up our responder chain is wrong, and delayed us from adding our hosting controller as a child until after the cell had been asked to self-size. Eventually, we would add our child view controller to some ancestor view controller, but it was too late, and the collection view would not ask us to self-size again without interacting with the collection view (scrolling it, forcing a layout invalidation some other way).Broken down into sequential steps, here are the things that happen to cause the bug:
collectionView(_:cellForItemAt:)
is calledEpoxySwiftUIHostingView
is created viamakeView()
contentView
via a call tosetViewIfNeeded(view:)
didMoveToSuperview
function is called, but prior to this change, we did nothing in this functioncollectionView(_:cellForItemAt:)
UICollectionView
calls the cell'spreferredLayoutAttributesFitting:
function, which returns an estimated sizedidMoveToWindow
function is called, and we finally add ourviewController
as a childUICollectionViewFlowLayout
orMagazineLayout
UICollectionViewCompositionalLayout
recovers more gracefully, due to it more aggressively invalidating specific index paths, leading to more self-sizing requests. Apple also uses an insane number of private APIs in that layout, so it's hard to know if MagazineLayout could also "get lucky" and avoid this issue or not. This issue is Epoxy's fault anyways, so probably not worth worrying about.One last note: Using the iOS 16+
UIHostingConfiguration
type also completely avoids these issues, likely because they get their view setup timing correct (as one would expect).How was it tested?
How did you verify that this change accomplished what you expected? Add more detail as needed.
Pull request checklist
All items in this checklist must be completed before a pull request will be reviewed.
CollectionViewConfiguration
CHANGELOG.md
entry in the "Unreleased" section for any library changes