From 4bad7d52604c695e48c0c161fed631aad8511449 Mon Sep 17 00:00:00 2001 From: Maxime Ollivier Date: Tue, 17 Sep 2024 07:11:55 -0700 Subject: [PATCH] add assert for iOS 18 supplementary view dequeue exceptions Summary: Same as last diff, but for supplementary views! Differential Revision: D62813794 fbshipit-source-id: f1630fd49740b1b34b422a0b4fd3055a3f0f0b21 --- Source/IGListKit/IGListAdapter.m | 21 ++++++++++++++++--- .../Internal/IGListAdapter+UICollectionView.m | 12 +++++++++++ .../Internal/IGListAdapterInternal.h | 1 + 3 files changed, 31 insertions(+), 3 deletions(-) diff --git a/Source/IGListKit/IGListAdapter.m b/Source/IGListKit/IGListAdapter.m index 518392bc7..42d146582 100644 --- a/Source/IGListKit/IGListAdapter.m +++ b/Source/IGListKit/IGListAdapter.m @@ -1223,7 +1223,7 @@ - (__kindof UICollectionReusableView *)dequeueReusableSupplementaryViewOfKind:(N [self.registeredSupplementaryViewIdentifiers addObject:identifier]; [collectionView registerClass:viewClass forSupplementaryViewOfKind:elementKind withReuseIdentifier:identifier]; } - return [collectionView dequeueReusableSupplementaryViewOfKind:elementKind withReuseIdentifier:identifier forIndexPath:indexPath]; + return [self _dequeueReusableSupplementaryViewOfKind:elementKind withReuseIdentifier:identifier forIndexPath:indexPath forSectionController:sectionController]; } - (__kindof UICollectionReusableView *)dequeueReusableSupplementaryViewFromStoryboardOfKind:(NSString *)elementKind @@ -1238,7 +1238,7 @@ - (__kindof UICollectionReusableView *)dequeueReusableSupplementaryViewFromStory UICollectionView *collectionView = self.collectionView; IGAssert(collectionView != nil, @"Dequeueing Supplementary View from storyboard of kind %@ with identifier %@ for section controller %@ without a collection view at index %li", elementKind, identifier, sectionController, (long)index); NSIndexPath *indexPath = [self indexPathForSectionController:sectionController index:index usePreviousIfInUpdateBlock:NO]; - return [collectionView dequeueReusableSupplementaryViewOfKind:elementKind withReuseIdentifier:identifier forIndexPath:indexPath]; + return [self _dequeueReusableSupplementaryViewOfKind:elementKind withReuseIdentifier:identifier forIndexPath:indexPath forSectionController:sectionController]; } - (__kindof UICollectionReusableView *)dequeueReusableSupplementaryViewOfKind:(NSString *)elementKind @@ -1257,7 +1257,22 @@ - (__kindof UICollectionReusableView *)dequeueReusableSupplementaryViewOfKind:(N UINib *nib = [UINib nibWithNibName:nibName bundle:bundle]; [collectionView registerNib:nib forSupplementaryViewOfKind:elementKind withReuseIdentifier:nibName]; } - return [collectionView dequeueReusableSupplementaryViewOfKind:elementKind withReuseIdentifier:nibName forIndexPath:indexPath]; + return [self _dequeueReusableSupplementaryViewOfKind:elementKind withReuseIdentifier:nibName forIndexPath:indexPath forSectionController:sectionController]; +} + +- (__kindof UICollectionReusableView *)_dequeueReusableSupplementaryViewOfKind:(NSString *)elementKind + withReuseIdentifier:(NSString *)identifier + forIndexPath:(NSIndexPath *)indexPath + forSectionController:(IGListSectionController *)sectionController { + // These will cause a crash in iOS 18 + IGAssert(_dequeuedSupplementaryViews.count == 0, @"Dequeueing more than one supplementary-view (%@) for indexPath %@, section controller %@,", identifier, indexPath, sectionController); + IGAssert(_isDequeuingSupplementaryView, @"Dequeueing a supplementary-view (%@) without a request from the UICollectionView for indexPath %@, section controller %@", identifier, indexPath, sectionController); + + UICollectionReusableView *const view = [self.collectionView dequeueReusableSupplementaryViewOfKind:elementKind withReuseIdentifier:identifier forIndexPath:indexPath]; + if (_isDequeuingSupplementaryView && view) { + [_dequeuedSupplementaryViews addObject:view]; + } + return view; } - (void)performBatchAnimated:(BOOL)animated updates:(void (^)(id))updates completion:(void (^)(BOOL))completion { diff --git a/Source/IGListKit/Internal/IGListAdapter+UICollectionView.m b/Source/IGListKit/Internal/IGListAdapter+UICollectionView.m index e350c135b..773470e44 100644 --- a/Source/IGListKit/Internal/IGListAdapter+UICollectionView.m +++ b/Source/IGListKit/Internal/IGListAdapter+UICollectionView.m @@ -77,6 +77,12 @@ - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cell - (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath { IGListSectionController *sectionController = [self sectionControllerForSection:indexPath.section]; id supplementarySource = [sectionController supplementaryViewSource]; + +#if IG_ASSERTIONS_ENABLED + if (!_dequeuedSupplementaryViews) { + _dequeuedSupplementaryViews = [NSMutableSet new]; + } +#endif // flag that a supplementary view is being dequeued in case it tries to access a supplementary view in the process _isDequeuingSupplementaryView = YES; @@ -84,6 +90,12 @@ - (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView _isDequeuingSupplementaryView = NO; IGAssert(view != nil, @"Returned a nil supplementary view at indexPath <%@> from section controller: <%@>, supplementary source: <%@>", indexPath, sectionController, supplementarySource); + + if (view && _dequeuedSupplementaryViews) { + // This will cause a crash in iOS 18 + IGAssert([_dequeuedSupplementaryViews containsObject:view], @"Returned a supplementary-view (%@) that was not dequeued at indexPath %@ from supplementary source %@", NSStringFromClass([view class]), indexPath, supplementarySource); + } + [_dequeuedSupplementaryViews removeAllObjects]; // associate the section controller with the cell so that we know which section controller is using it [self mapView:view toSectionController:sectionController]; diff --git a/Source/IGListKit/Internal/IGListAdapterInternal.h b/Source/IGListKit/Internal/IGListAdapterInternal.h index b813d313c..5d22cfc25 100644 --- a/Source/IGListKit/Internal/IGListAdapterInternal.h +++ b/Source/IGListKit/Internal/IGListAdapterInternal.h @@ -34,6 +34,7 @@ IGListBatchContext NSMutableSet *_dequeuedCells; BOOL _isDequeuingSupplementaryView; + NSMutableSet *_dequeuedSupplementaryViews; BOOL _isSendingWorkingRangeDisplayUpdates; }