There is one strange crash in Firebase Crashlytics logs that occurs quite often, saying:
Crashed: com.apple.main-thread
0 AppName 0xd454 SomeVC.tableView(_:cellForRowAt:) 90 (SomeVC.swift:90)
1 AppName 0xd500 @objc SomeVC.tableView(_:cellForRowAt:) 4300477696 (<compiler-generated>:4300477696)
2 UIKitCore 0x285f20 -[UITableView _createPreparedCellForGlobalRow:withIndexPath:willDisplay:] 1532
3 UIKitCore 0x483330 -[UITableView _updateVisibleCellsForRanges:createIfNecessary:] 732
4 UIKitCore 0x2ab118 -[UITableView _updateVisibleCellsNow:] 1432
5 UIKitCore 0x17b5a0 -[UITableView layoutSubviews] 456
6 UIKitCore 0x18b844 -[UIView(CALayerDelegate) layoutSublayersOfLayer:] 2592
7 QuartzCore 0x401c0 CA::Layer::layout_if_needed(CA::Transaction*) 532
8 QuartzCore 0x325fc CA::Layer::layout_and_display_if_needed(CA::Transaction*) 136
9 QuartzCore 0x46f70 CA::Context::commit_transaction(CA::Transaction*, double, double*) 452
10 QuartzCore 0x4fe78 CA::Transaction::commit() 704
11 QuartzCore 0x31d7c CA::Transaction::flush_as_runloop_observer(bool) 88
12 UIKitCore 0x53d9d8 _UIApplicationFlushCATransaction 72
13 UIKitCore 0x7d8084 _UIUpdateSequenceRun 84
14 UIKitCore 0xe5dcb0 schedulerStepScheduledMainSection 144
15 UIKitCore 0xe5d478 runloopSourceCallback 92
16 CoreFoundation 0xbbf04 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ 28
17 CoreFoundation 0xccc90 __CFRunLoopDoSource0 208
18 CoreFoundation 0x6184 __CFRunLoopDoSources0 268
19 CoreFoundation 0xbb4c __CFRunLoopRun 828
20 CoreFoundation 0x1f6b8 CFRunLoopRunSpecific 600
21 GraphicsServices 0x1374 GSEventRunModal 164
22 UIKitCore 0x513e88 -[UIApplication _run] 1100
23 UIKitCore 0x2955ec UIApplicationMain 364
24 AppName 0x57e4 main 15 (OnboardingViewModel.swift:15)
25 ??? 0x100ef9ce4 (Missing)
Line 90 in SomeVC
points to
switch indexPath.row {
The only thing that unites the users is how much RAM is left, seems it's happened when the user has low memory:
User 1, RAM free: 45.75 MB
User 2, RAM free: 64.48 MB
User 3, RAM free: 41.34 MB
User 4, RAM free: 29.56 MB
User 5, RAM free: 61.3 MB
Everything else is different: device type, iOS version, etc.
Also, I can't find a way how to reproduce this crash even I have a full log of analytics events that happened before. Nothing crazy, they just using the app as the rest users without this crash.
I've tried to "Simulate memory warning" option as well fully filling the simulator's RAM by creating some fillings functions, but nothing helps to reproduce this case.
Maybe anyone has ideas on how to reproduce or fix this crash, or understand what can be the cause of that?
Thank you!
UPDATE
the function that provides the crash at the line starting with switch
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let vm = vm, let currentModel = vm.model else { return UITableViewCell() }
switch indexPath.row {
case 0:
guard let cell = tableView.dequeueReusableCell(withIdentifier: "HeaderCell", for: indexPath) as? HeaderCell else { return UITableViewCell() }
cell.setup(images: currentModel.images)
cell.selectionStyle = .none
return cell
case 1:
guard let cell = tableView.dequeueReusableCell(withIdentifier: "DescriptionCell", for: indexPath) as? DescriptionCell else { return UITableViewCell() }
cell.setup(vc: self, id: currentModel.id, rating: currentModel.rating, description: currentModel.description)
cell.selectionStyle = .none
return cell
case correctCount()...correctCount() currentModel.files.count:
guard let cell = tableView.dequeueReusableCell(withIdentifier: "ContentCell", for: indexPath) as? ContentCell else { return UITableViewCell() }
cell.setupUI(id: currentModel.id, file: currentModel.files[indexPath.row - correctCount()], vc: self)
cell.selectionStyle = .none
return cell
case 2:
guard let cell = tableView.dequeueReusableCell(withIdentifier: "BannerCell", for: indexPath) as? BannerCell,
let adLoader = adLoader,
let nativeAd = nativeAd else { return UITableViewCell() }
cell.backgroundColor = .clear
cell.setUp(adLoader, didReceive: nativeAd)
cell.selectionStyle = .none
return cell
default:
return UITableViewCell()
}
}
CodePudding user response:
You have posted very little information to go on, but based on what little there is the only suspicious bit of code is the correctCount
section.
Based on the assumption it is accessing an out of index item in the array this should solve it in that case. However, it is also possible it could be correctCount()
itself which is causing the crash, as that is unknown what that is.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let vm = vm, let currentModel = vm.model else { return UITableViewCell() }
let correct_count = correctCount()
let filesCount = currentModel.files.count
switch indexPath.row {
case 0:
guard let cell = tableView.dequeueReusableCell(withIdentifier: "HeaderCell", for: indexPath) as? HeaderCell else { return UITableViewCell() }
cell.setup(images: currentModel.images)
cell.selectionStyle = .none
return cell
case 1:
guard let cell = tableView.dequeueReusableCell(withIdentifier: "DescriptionCell", for: indexPath) as? DescriptionCell else { return UITableViewCell() }
cell.setup(vc: self, id: currentModel.id, rating: currentModel.rating, description: currentModel.description)
cell.selectionStyle = .none
return cell
case correct_count...(correct_count filesCount):
guard let cell = tableView.dequeueReusableCell(withIdentifier: "ContentCell", for: indexPath) as? ContentCell else { return UITableViewCell() }
let fileIndex = indexPath.row - correct_count
guard fileIndex < currentModel.files.count else { return UITableViewCell() }
let file = currentModel.files[fileIndex]
cell.setupUI(id: currentModel.id, file: file, vc: self)
cell.selectionStyle = .none
return cell
case 2:
guard let cell = tableView.dequeueReusableCell(withIdentifier: "BannerCell", for: indexPath) as? BannerCell,
let adLoader = adLoader,
let nativeAd = nativeAd else { return UITableViewCell() }
cell.backgroundColor = .clear
cell.setUp(adLoader, didReceive: nativeAd)
cell.selectionStyle = .none
return cell
default:
return UITableViewCell()
}
}
This would prevent a crash if correctCount and the indexPath's get out of sync. However, if they are getting out of sync that likely means your table view isn't being updated when the data changes.
A better approach might be to instead calculate correctCount()
every time the data changes, and the table view needs to be updated. Then have the table view code use that variable instead of recalculating it.
Then always update the data, the correctCount variable, and then the table view together in the same place, in that order, so they can not get out of sync in the first place.
If the currentModel.files
array is dynamic and changes, the table view should likely be loading data from a local copy of the files array, and the ViewController should be listening for changes to the file array and updating the table view data to show the new data.
In general you always want something like a UITableView to be pointing to a local static
copy of data, instead of the real dynamic data itself. As otherwise the table view, and it's index path calculations, can get out of sync with reality and start trying to access items in the array which just don't exist.