10.0 (14A345): collection view crash when removing item from data source and reloading
| Originator: | igeek1 | ||
| Number: | rdar://28300578 | Date Originated: | 14-Sep-2016 11:31 AM |
| Status: | Open | Resolved: | |
| Product: | iOS | Product Version: | 10.0 (14A345) |
| Classification: | Crash/Hang/Data Loss | Reproducible: | Always |
Summary:
If you remove an item from a collection view’s data source, and then ask the collection view to reload, it will crash.
Steps to Reproduce:
1. Open the attached sample project “Gud Tay/Gud Tay.xcworkspace” (NOTE: the workspace, NOT the project) in Xcode 8.0 (8A218a)
2. Under Gud_TayUITests.swift, run the testSwitchingFromRainToNoRain() test.
Expected Results:
No crash. The cell with Calvin in it appears and disappears continuously. The test won’t pass, because I’ve introduced a delay to make debugging easier, but the point is that the app does not crash.
Actual Results:
The app crashes:
2016-09-14 11:26:57.750 Gud Tay[35476:2136479] *** Assertion failure in -[UICollectionViewData validateLayoutInRect:], /BuildRoot/Library/Caches/com.apple.xbs/Sources/UIKit_Sim/UIKit-3599.6/UICollectionViewData.m:433
2016-09-14 11:26:57.758 Gud Tay[35476:2136479] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'UICollectionView received layout attributes for a cell with an index path that does not exist: <NSIndexPath: 0xc000000000000016> {length = 2, path = 0 - 0}'
*** First throw call stack:
(
0 CoreFoundation 0x000000010c7c634b __exceptionPreprocess + 171
1 libobjc.A.dylib 0x000000010c22721e objc_exception_throw + 48
2 CoreFoundation 0x000000010c7ca442 +[NSException raise:format:arguments:] + 98
3 Foundation 0x000000010bdbdedd -[NSAssertionHandler handleFailureInMethod:object:file:lineNumber:description:] + 195
4 UIKit 0x000000010df001fb __45-[UICollectionViewData validateLayoutInRect:]_block_invoke + 1804
5 UIKit 0x000000010deff4e3 -[UICollectionViewData validateLayoutInRect:] + 2984
6 UIKit 0x000000010dea5d12 -[UICollectionView layoutSubviews] + 232
7 UIKit 0x000000010d61d344 -[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 1237
8 QuartzCore 0x000000010d3cfcdc -[CALayer layoutSublayers] + 146
9 QuartzCore 0x000000010d3c37a0 _ZN2CA5Layer16layout_if_neededEPNS_11TransactionE + 366
10 QuartzCore 0x000000010d3c361e _ZN2CA5Layer28layout_and_display_if_neededEPNS_11TransactionE + 24
11 QuartzCore 0x000000010d35162c _ZN2CA7Context18commit_transactionEPNS_11TransactionE + 280
12 QuartzCore 0x000000010d37e713 _ZN2CA11Transaction6commitEv + 475
13 QuartzCore 0x000000010d37f083 _ZN2CA11Transaction17observer_callbackEP19__CFRunLoopObservermPv + 113
14 CoreFoundation 0x000000010c76ae17 __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 23
15 CoreFoundation 0x000000010c76ad87 __CFRunLoopDoObservers + 391
16 CoreFoundation 0x000000010c74fb9e __CFRunLoopRun + 1198
17 CoreFoundation 0x000000010c74f494 CFRunLoopRunSpecific + 420
18 GraphicsServices 0x000000011124ca6f GSEventRunModal + 161
19 UIKit 0x000000010d558f34 UIApplicationMain + 159
20 Gud Tay 0x000000010b2176cf main + 111
21 libdyld.dylib 0x000000011183468d start + 1
22 ??? 0x0000000000000001 0x0 + 1
)
libc++abi.dylib: terminating with uncaught exception of type NSException
Regression:
I have a report from someone with the same problem that this was working in iOS 9, but I can’t confirm because my app started on iOS 10. Hopefully I can get that person to dupe this with their own sample code, but I’m not sure.
Notes:
Ignore all the MBTA (Boston subway system) view controllers, view models, and services. They’re not related to this bug; I just didn’t have time to strip them out for the sample app. All we’re concerned with is the collection view along the bottom, which shows weather information.
When running UI unit tests, MockWeatherService is used to provide conditions alternating between raining and not raining. The first time the weather switches from raining to not raining, WeatherViewModel updates and returns zero objects in the completion block of processForecast(_:). The collection view is subsequently updated at WeatherViewController.swift:80 via self.collectionView.realoadData(). This _should_ invalidate the layout as well, but it apparently does not, because as you can see from the exception, the collection view received attributes for element (0, 0), which should not exist when the collection view is empty.
The issue can be fixed by invalidating the layout, but only _after_ calling reloadData. You can see this in the comments in WeatherViewController.swift, lines 77–78 and 82–83. It seems that you shouldn’t have to invalidate the layout at all, and it’s also weird that he order matters.
Sample project: https://cl.ly/hSfy
Comments
Please note: Reports posted here will not necessarily be seen by Apple. All problems should be submitted at bugreport.apple.com before they are posted here. Please only post information for Radars that you have filed yourself, and please do not include Apple confidential information in your posts. Thank you!
You are not the only one experiencing this exact issue.