Search code examples
swiftmacosnstableview

TableView Index out of range issue


My view controller opens a directory, counts the file types therein and stores the results in a dictionary [String:Int] of filetypes and count. I have a TableView that displays this.

The first time I open a directory the ViewController correctly displays the information in the FileTypeTableview. If I try to open another directory, execution gets to the line:

let dialogButton = dialog.runModal()

Then immediately jumps to a line in my TableView function and throws an index out of range here:

val = types[row]

Types is a [String] and shows 0 elements and row is an Int and shows 0. I'm confused by the fact this happens at the dialog.runModal() function call.

Below are the functions that get the directory and display the TableViews. I'm very new to Swift and MacOS programing and I'd appreciate any insights.

@IBAction func getFolder(_ sender: Any) {
    
    let dialog = NSOpenPanel()
    dialog.canChooseDirectories = true
    dialog.canChooseFiles = false
    
    allFiles = []
    fileTypesDict.removeAll()
    
    let dialogButton = dialog.runModal()
    
    if let theURL = dialog.url {
    theDirectory = theURL.path
    allFilesAsPaths = getAllFiles2(atDirectoryPath: theURL.path)
    allFilesAsPaths.sort()
        
    }
    
    fileCountLabel.stringValue = String(allFilesAsPaths.count)
    
    mainTableView2.reloadData()
    fileTypeTableView.reloadData()
}



func numberOfRows(in tableView: NSTableView) -> Int {
            
    var numberOfRows : Int = 0
    
    if tableView == mainTableView2 {
        print("number of rows for mainTableView2")
        numberOfRows = allFilesAsPaths.count
    } else if tableView == fileTypeTableView {
        print("number of rows for fileTypeTableView")
        numberOfRows = fileTypesDict.count
    } else if tableView == exifTableView {
        numberOfRows = exifData.count
        print("exifData.count = \(exifData.count)")
        print("number of rows for exifTableview \(numberOfRows)")
    }
    return numberOfRows
}

func tableView(_ tableView: NSTableView, objectValueFor tableColumn: NSTableColumn?, row: Int) -> Any? {
    
    var val : String = ""
    
    if tableView == mainTableView2 {
    val = allFilesAsPaths[row]
    }
    if tableView == exifTableView {
        if tableColumn?.identifier.rawValue == "tag" {
            let tagArray : [String] = Array(exifData.keys)
            val = tagArray[row]
        } else if tableColumn?.identifier.rawValue == "value" {
            let valueArray : [String] = Array(exifData.values)
            val = valueArray[row]
        }
    }
    
    if tableView == fileTypeTableView {

    let types : [String] = Array(fileTypesDict.keys)
    let counts : [Int] = Array(fileTypesDict.values)

    if tableColumn?.identifier.rawValue == "type" {
            val = types[row]
        } else {
            val = String(counts[row])
        }
    }
    return val
}

Solution

  • I think you should try to remove

    allFiles = []
    fileTypesDict.removeAll()
    

    And simply set all your arrays at once after dialog validation. Because it looks like presenting the dialog triggers an update of the table, but at this time, your objects are not sync because you do some work before, and after.

    BTW, it would be clearer if you do separate table classes than dealing with if tableView == ... {}. You take the risk that your code gradually become inextricable. I've been there already ;)