Crashed at DidSelectRowAt

Crashed: com.apple.main-thread
0  MyAppName                       0x100b9f598 specialized SearchVC.tableView(UITableView, didSelectRowAt : IndexPath) -> () (SearchVC.swift:1583)
1  MyAppName                       0x100b902ec @objc SearchVC.tableView(UITableView, didSelectRowAt : IndexPath) -> () (SearchVC.swift)
2  UIKit                          0x18bb36edc -[UITableView _selectRowAtIndexPath:animated:scrollPosition:notifyDelegate:] + 1356
3  UIKit                          0x18bb8ab60 -[UITableView _userSelectRowAtPendingSelectionIndexPath:] + 268
4  UIKit                          0x18bc15884 _runAfterCACommitDeferredBlocks + 296
5  UIKit                          0x18bc0b958 _cleanUpAfterCAFlushAndRunDeferredBlocks + 384
6  UIKit                          0x18baf051c _afterCACommitHandler + 132
7  CoreFoundation                 0x181bc6910 __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 32
8  CoreFoundation                 0x181bc4238 __CFRunLoopDoObservers + 412
9  CoreFoundation                 0x181bc4884 __CFRunLoopRun + 1436
10 CoreFoundation                 0x181ae4da8 CFRunLoopRunSpecific + 552
11 GraphicsServices               0x183aca020 GSEventRunModal + 100
12 UIKit                          0x18bb04758 UIApplicationMain + 236
13 MyAppName                       0x100b65214 main (History+CoreDataProperties.swift:15)
14 libdyld.dylib                  0x181575fc0 start + 4


The content above is where I receive the report from fabric. However, I do not really understand where does the crash happens. The only thing I understand from above is user did select a row and it returns empty. I checked my code, I did make sure all the array are set to empty first before append new stuffs and also reload the table view in main thread. Sorry for my knowledge, I am a just a beginner iOS developr. Many thanks in advance for helping me to solve this error.

Replies

It's hard to say something sure, please show as much relevant code as you can.

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        
        if wordLists.count != 0 {
            let currentRow = wordLists[indexPath.row]
            
            DispatchQueue.main.async {
                self.textField.text = currentRow
            }
            
            let currentWordToSearch = currentRow.condenseWhitespace().lowercased()
            DispatchQueue.main.async {
                self.indicator.startAnimating()
            }
            
            saveWordToHistory(currentWordToSearch) // To save the word in History
            fetchWordFromDB(currentWordToSearch) // To check whether this word has been favourite before or not
            wordChange(wordToEncode: currentWordToSearch, uuid: getDeviceID()) // Custom function to encode and display the
            //result via webview.
            
            wordLists = []
            
            DispatchQueue.main.async {
                self.tableView.reloadData()
                self.tableView.isHidden = true
                self.textField.resignFirstResponder()
            }
            
            setupDatabase(suggWords: currentWordToSearch) //Return sugguestions and store into wordLists array and display it
            // via tableview.
            
        }
    }


This is my partially code for didSelectRowAt. Some of the code are not appriopriate to share it out. Hope you understand it. Thanks!

Your backtrace indicates that the crash occurred on line 1583 of

SearchVC.swift
.
0 MyAppName 0x100b9f598 specialized SearchVC.tableView(UITableView, didSelectRowAt : IndexPath) -> () (SearchVC.swift:1583)

Is that line included in the code snippet you posted. If so, what line is it?

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {  

        if wordLists.count != 0 {  
            let currentRow = wordLists[indexPath.row]  

            DispatchQueue.main.async {  
                self.textField.text = currentRow  
            }  

            let currentWordToSearch = currentRow.condenseWhitespace().lowercased()  
            DispatchQueue.main.async {  
                self.indicator.startAnimating()  
            }  

            saveWordToHistory(currentWordToSearch) // To save the word in History  
            fetchWordFromDB(currentWordToSearch) // To check whether this word has been favourite before or not  
            wordChange(wordToEncode: currentWordToSearch, uuid: getDeviceID()) // Custom function to encode and display the  
            //result via webview.  

            wordLists = []  

            DispatchQueue.main.async {  
                self.tableView.reloadData()  
                self.tableView.isHidden = true  
                self.textField.resignFirstResponder()  
            }  

            setupDatabase(suggWords: currentWordToSearch) //Return sugguestions and store into wordLists array and display it  
            // via tableview.  

       }  //LINE 1583 
    }

Hi, thank you for your response, I got a backup file stated the line 1583 at the end of the function. Please refer the line 1583 above. Thank you very much!

Thanks for showing your code. But, unfortunately, it's still very dfficult to say something sure for me.


If you say you cannot show any more, just guesses.


1. `indexPath.row` may be >= `wordLists.count`, at line 04.


This sort of inconsistensy may happen when your `tableView(_:numberOfRowsInSection:)` and/or `tableView(_:cellForRowAt:)` is complex enough. Or in cases your `wordLists` may be updated from various parts of your code.


2. If your `setupDatabase(suggWords:)` updates your `wordLists` concurrently, that may be conflicting with `self.tableView.reloadData()`.


I expect more stack info should be output in this case, but I'm not familiar with fabric report and do not know how much I can rely on it.


These are something I can think of as for now.

You are welcome. I am the one should say thank you!


func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        
        return wordLists.count
    }
    
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        
        guard let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as? SearchCell else {
            return UITableViewCell()
        }
        
        if wordLists.count != 0 {
            let currentRow = wordLists[indexPath.row]
            
            cell.resultLabel.text = currentRow
        }
        
        return cell
    }


//Partially code in my setupDatabase(suggWords)

if let tableName = databaseName {
            do {
                let querySQL = "Some sql query"
                
                let results:FMResultSet = try database.executeQuery(querySQL, values: nil)
                while results.next() {
                    if let x = results.string(forColumn: "some column keyword") {
                        wordLists.append(x)
                    }
                }
                
                if isFromWebView == false {
                    if wordLists.count == 0 {
                        DispatchQueue.main.async {
                            self.tableView.isHidden = true
                            self.tableView.reloadData()
                        }
                    } else {
                        DispatchQueue.main.async {
                            self.tableView.reloadData()
                        }
                    }
                } else {
                    if wordLists.count == 0 {
                        DispatchQueue.main.async {
                            self.tableView.isHidden = true
                            self.tableView.reloadData()
                        }
                        isFromWebView = false
                    } else {
                        DispatchQueue.main.async {
                            self.tableView.isHidden = true
                            self.tableView.reloadData()
                        }
                        isFromWebView = false
                    }
                }
                
                results.close()
                
            } catch {
//                print("failed: \(error.localizedDescription)")
            }
        }


Hi, May I know that the code I provided above is sufficient enough to provide you some clues?
Thank you for replying me!

I cannot be sure if any of these can be the cause of your issue, but your implementation has some faults.


First, you are not implementing `tableView(_:cellForRowAt:)` properly.


In this line:

    guard let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as? SearchCell else {

you expect the cell type for the identifier "cell" as a SearchCell, but you create and return a UITableViewCell.


When dequed cell is nil, you need to create a cell of the same type, in your case, the type is SearchCell:

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        var cell = tableView.dequeueReusableCell(withIdentifier: "cell") as? SearchCell
        if cell == nil {
            cell = SearchCell()
        }
        if wordLists.count != 0 {
            let currentRow = wordLists[indexPath.row]
            cell!.resultLabel.text = currentRow
        }
        return cell!
    }


Or you can use `dequeueReusableCell(withIdentifier:for:)` instead of `dequeueReusableCell(withIdentifier:)`:

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! SearchCell
        if wordLists.count != 0 {
            let currentRow = wordLists[indexPath.row]
            cell.resultLabel.text = currentRow
        }
        return cell
    }


Second, your `wordLists` may be simulltaneously accessed or updated. Swift Arrays are not thread-safe.

If yout partial code may be executed in a background thread, it can make your app crash.


You have so many small `DispatchQueue`s. You should better execute all your code in a single bigger UI thread.

guard let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as? SearchCell else {  
            return UITableViewCell()  
        }

For this I thought that if the cell is able to create SearchCell successfully then all will be using SearchCell else if fail to create then return an empty UITableViewCell() ? So it would not force crash the app.


1. "when dequed cell is nil, you need to create a cell of the same type, in your case, the type is SearchCell:"

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {  
        var cell = tableView.dequeueReusableCell(withIdentifier: "cell") as? SearchCell  
        if cell == nil {  
            cell = SearchCell()  
        }  
        if wordLists.count != 0 {  
            let currentRow = wordLists[indexPath.row]  
            cell!.resultLabel.text = currentRow  
        }  
        return cell!  
    } 

Can I use this instead? Because I remember that the ! sign will crash the app immediately if anything happens. Or both are working as well?

guard let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as? SearchCell else {  
            return SearchCell()  
}


2. Second, your `wordLists` may be simulltaneously accessed or updated. Swift Arrays are not thread-safe.

If yout partial code may be executed in a background thread, it can make your app crash.

- The partial code that I provided is the only part to append it to array. I think you are right, they may be executed in a background thread. May I know that any ideas to solve it?


Thank you for your response, much appreciated. 🙂

guard let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as? SearchCell else {    
            return UITableViewCell()    
        }  

For this I thought that if the cell is able to create SearchCell successfully then all will be using SearchCell else if fail to create then return an empty UITableViewCell() ? So it would not force crash the app.

It is very unlikely that this code may cause crash, just that it is not a proper implementation.


You expect the identifier "cell" as SearchCell, although you are returning UITableViewCell. This does not cause crash immediately, but this sort of mismatch can be a potential risk.


But, bad thing is not only the type of returned cell, you return a empy cell for a specific indexPath when reusable cell is not available.

Do you think your table view can show users an empty cell even if there is a content for the cell?

Just not crash is your app's purpose? You think you can show blank empty cells occasionally?


Can I use this instead? Because I remember that the ! sign will crash the app immediately if anything happens. Or both are working as well?

The answer is no. You are wrong with this: if anything happens. It will crash your app if the Optional is nil. Not if anything happens.

Do you think `cell` can be nil after this code?

        if cell == nil {
            cell = SearchCell()
        }

Please be free from some illusion such as nerver use something. Something you should never use would not be included into language feature.


Assume `SearchCell()` might return nil, then your `return SearchCell()` could also cause crash, as iOS expects non-nil value. Also, I need to ask you: Do you want an empty cell to be displayed?


If you cannot be sure when it's safe to use forced unwrapping !, you should better use `dequeueReusableCell(withIdentifier:for:)`.

(I have missed to include, but you need to register the class for identifier "cell" somewhere. Maybe in `viewDidLoad()`.)


The partial code that I provided is the only part to append it to array. I think you are right, they may be executed in a background thread. May I know that any ideas to solve it?

As I wrote, move all parts into a single `Dispatch.main.async {...}`. I cannot show you a concrete example as you are showing just partial code of your method.

Thank you very much for your time and patience. I will try to implement what you stated above. One last question, I am not sure whether my array is executed in a background thread. The code that i showed you previously was the only part to append it to array, remaining partial code is about to do check condition and which table name to use. I thought only UIKit is allowed to be in "DispatchQueue.main.async {}"? If i put "DispatchQueue.main.async { append the words into the array }", will it be most likely to have crashed ? I thought it is not safe to do such action in main thread ?

Sorry to be late.


I may be misaking what you mean, but...


I thought only UIKit is allowed to be in "DispatchQueue.main.async {}"? If i put "DispatchQueue.main.async { append the words into the array }", will it be most likely to have crashed ? I thought it is not safe to do such action in main thread ?


I do not understand why you think such things can be true. Be free from such illusions and write code based on the documented behavior of iOS or Swift.

Or have you ever read any sort of documentation describing such behaviors?

Alright, I will do more research for the next time. Thank you very much! 🙂

Hi, I follow your suggestion by using `dequeueReusableCell(withIdentifier:for:)` instead of `dequeueReusableCell(withIdentifier:)`:, and now I received some errors like this

Crashed: com.apple.main-thread
0  myApp                          0x10461882c specialized SearchVC.tableView(UITableView, cellForRowAt : IndexPath) -> UITableViewCell (SearchVC.swift:1706)
1  myApp                         0x10460b468 @objc SearchVC.tableView(UITableView, cellForRowAt : IndexPath) -> UITableViewCell (SearchVC.swift)
2  UIKit                          0x18eb0c0cc -[UITableView _createPreparedCellForGlobalRow:withIndexPath:willDisplay:] + 668
3  UIKit                          0x18eb0bd84 -[UITableView _createPreparedCellForGlobalRow:willDisplay:] + 80
4  UIKit                          0x18eb0aaa0 -[UITableView _updateVisibleCellsNow:isRecursive:] + 2280
5  UIKit                          0x18eb065ec -[UITableView layoutSubviews] + 140
6  UIKit                          0x18ea436f4 -[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 1420
7  QuartzCore                     0x188fb9fec -[CALayer layoutSublayers] + 184
8  QuartzCore                     0x188fbe17c CA::Layer::layout_if_needed(CA::Transaction*) + 324
9  QuartzCore                     0x188f2a830 CA::Context::commit_transaction(CA::Transaction*) + 320
10 QuartzCore                     0x188f52364 CA::Transaction::commit() + 580
11 QuartzCore                     0x188ea2ea8 CA::Display::DisplayLink::dispatch_items(unsigned long long, unsigned long long, unsigned long long) + 288
12 QuartzCore                     0x188f58490 display_timer_callback(__CFMachPort*, void*, long, void*) + 240
13 CoreFoundation                 0x184dc0b20 __CFMachPortPerform + 188
14 CoreFoundation                 0x184ddbae8 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__ + 56
15 CoreFoundation                 0x184ddb230 __CFRunLoopDoSource1 + 440
16 CoreFoundation                 0x184dd8c80 __CFRunLoopRun + 2456
17 CoreFoundation                 0x184cf8da8 CFRunLoopRunSpecific + 552
18 GraphicsServices               0x186cdd020 GSEventRunModal + 100
19 UIKit                          0x18ed15758 UIApplicationMain + 236
20 myApp                          0x1045dfa30 main (History+CoreDataProperties.swift:16)
21 libdyld.dylib                  0x184789fc0 start + 4


for coding part,

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
       
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! SearchCell
        
        if wordLists.count != 0 {
            let currentRow = wordLists[indexPath.row]
            
            cell.resultLabel.text = currentRow
        }
        
         return cell //line 1706
    }


May I know what kind of problem is it?