[EDIT]:
I think I have solved the lockup issue. While refactoring the didSelectRowAt
prep for exit to be a blocking task in the ping async queue, I noticed that successful transitions would consist of one instantaneous move to rootVC followed by another animated transition to rootVC again. Made me realize I had two segues from my tableVC: one being the generic controller to controller segue (which is the intended segue method) and one from selecting the table cell.
I am unsure whether it was just the removal of the extra segue or the combination of multiple attempted solutions AND the removal of the segue that solved the problem. My guess is the latter as memory tells me that there was a time where there was only one segue and "fixes" weren't working then either.
tl;dr: Make sure you have no competing segues and that your async code is thread safe/has proper clean up.
[PROBLEM]:
I have a TableViewController which is a child VC of a rootVC under a Navigation Controller. In other words NavController[rootVC, tableVC].
The app starts in rootVC and is segued into tableVC through a UIButton. At tableVC's viewDidLoad, a network scan is done if no previous scan was completed before. Devices on the network are pinged at a certain port asynchronously as they are discovered. If these pings are successful, the IP Address associated with the device is used to populate a cell in the Table View. If this cell is selected, some app settings are changed through didSelectRowAt indexPath and an unwind segue is done to return to rootVC. viewWillDisappear is utilized as well to pass on values through isMovingFromParentViewController (previously done through prepare for segue but error was also present). The user is able to select the IP Address cell as soon as it appears in the table view, which is intended design.
That functionality all works. Most of the time. However, the app locks up SOMETIMES after a cell is selected WHEN a cell is selected while scanning is not done (this would mean that there can still be async pings happening/queued). I have a print at the beginning of didSelectAtRow which never happens when the lock up occurs. I also have an activity indicator view in the table view, which continues spinning when everything else is unresponsive. There is no crash or break from Xcode's side. It seems like that didSelectAtRow just does not happen and cannot proceed for some reason.
This is how my didSelectAtRow looks like
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("Cell was selected")
if possibleDevices.count != 0 {
selectedTableCellAddress = possibleDevices[indexPath.row]
let defaults = UserDefaults.standard
// Set the current cell's IP Address to the setting's IP Address
if selectedTableCellAddress != nil {
pingOperationsShouldStop = true
lanScanner.stop()
defaults.set(selectedTableCellAddress, forKey: "address")
}
}
}
Note that lanScanner is an instance of MMLanScan
My rookie knowledge gives me a guess that this is due to my async pings (the ping results are the last things that appear on the console). The async functions happen when a device is found on the LAN. It is encapsulated in a function like this:
DispatchQueue.global(qos: .userInteractive).async {
print("pingOperationsShouldStop? \(self.pingOperationsShouldStop)")
if self.pingOperationsShouldStop {
print("Skipping ping for \(addr)")
return
}
self.pingOperationsHaveStopped = false
let port = Int32(self.portStr)
let client = TCPClient(address: address, port: port!)
switch client.connect(timeout: 1) {
case .success:
print("connected")
DispatchQueue.main.sync {
self.possibleDevices.append(address)
self.numberOfPossibleDevicesCounted += 1
self.pingOperations.leave()
}
client.close()
case .failure(let error):
print("failed due to \(error) for \(addr)")
DispatchQueue.main.sync {
self.numberOfPossibleDevicesCounted += 1
client.close()
self.pingOperations.leave()
}
}
}
Note that possibleDevices has a didSet property observer that calls reloadData.
At first I thought that it only occurred through cell selection (i.e. NavCon's Back worked fine). So I programmatically called the popping of the view, to not avail. I tried reproducing the lock up through NavCon's Back button and have been able to do it once.
I'm not sure what code is relevant to share, so let me know if you need more information.
Thanks!