On iPadOS and iOS13 on a call to visibleCells on a UITableView ( on code which works fine on iOS9 to iOS12 ) I get an exception with the message
Attempted to access the table view's visibleCells while they were in the process of being updated, which is not allowed.
Searching on the web gives me no hits for this exact message, so I suspect the message itself is new or changed.
Now the general gist of the message is fairly clear, but the specifics and but how one is expected to avoid this in a multithreaded application are far from clear.
For example ...what sort of changes qualify,
For example ... the user may rotate the device, a redisplay may be caused by incoming data. Are these now to be expected to crash your application?
Maybe there is a new threading model / usage requirement that I don't know about, yet.
Synchronising on the UITableView itself doesn't help.
What am I missing
And Yes, I can avoid the problem by catching the error and backing off until I succeed. But the hit on performance is eyewatering. And it's horrible.
This is a new exception in iOS 13 that UITableView will raise in order to prevent and proactively alert you of a situation that would previously cause undefined behavior and a variety of strange, seemingly unrelated, and hard-to-debug issues (including crashes).
What is happening here is that UITableView is in the middle of asking its dataSource to return a cell for each visible row and is configuring the properties of the returned cells so they can be displayed. And in the middle of this updating -- most likely inside a callback from the table view itself about a specific row such as tableView(_:cellForRowAt:) tableView(_:canEditRowAt:), etc -- your code is asking the table view to return the visibleCells. This is obviously problematic, because UITableView is right in the middle of preparing those cells, so it cannot possibly return a meaningful answer.
The fix for this is to look at where you are calling visibleCells in the backtrace when this exception is raised, and then do one of two things:
Option 1: Move the usage of visibleCells to a better place, so that you aren't asking for the visibleCells from some place that is called during the process of creating/configuring/updating those same cells. A great place to ask for the visible cells is after the table view lays out, so for example if the table view is the view of a view controller you can use viewDidLayoutSubviews(), or in a subclass of UITableView do it after calling super.layoutSubviews().
Option 2: Depending on what you're actually trying to do, you might be able to skip using visibleCells altogether. For example, you might be able to leverage the callbacks tableView(_:willDisplay:forRowAt:) and tableView(_:didEndDisplaying:forRowAt:) to track when cells are visible instead.
Another place you may hit this is if you are using Key-Value Observation to observe the table view's contentOffset directly, and are asking for the visibleCells every time that changes. You should use the UIScrollView delegate method scrollViewDidScroll(_:) instead, or possibly a different approach altogether (such as the suggestion in Option 2 above).
If you are hitting this exception and you think you are requesting the visibleCells from a location that should be valid/allowed, please share the backtrace when you hit this exception and details about what you're trying to do.