Search code examples
iosswiftnsmutablearraysubscript

Wanted to see if anyone has any insight to why an "ambigious use of subscript" error is created in one project vs. another in swift


So based on the following project tutorial expandableCells, using sub-scripting with NSMutableArray works. (I opened the project myself in xcode and get no errors)

When I try to utilize this workflow in my own project, I get "ambiguous use of subscript" errors all over the place. This is the same issue asked in a previous question Ambiguous use of subscript

My question would be why would the project provided by appcoda work in xcode but similar code not work when trying to utilize a similar workflow in a new project. Now mind you, the issue appears to be how swift is handling NSMutableArray because when I rewrite the code as a swift array and dictionary, everything works except there is no easy way to convert the plist to a swift array.

My plist is in the same format as the tutorial: Array, Array, Dictionary

Here is a snip where I am seeing the error

var cellDescriptors: NSMutableArray!

func loadSections() {

    let path: String = NSBundle.mainBundle().pathForResource("NewCells", ofType: "plist")!
    cellDescriptors = NSMutableArray(contentsOfFile: path)
    getIndicesOfVisibleRows()
    tblExpandable.reloadData()
}

func getIndicesOfVisibleRows() {
    visibleRowsPerSection.removeAll()

    for currentSectionCells in cellDescriptors {
        var visibleRows = [Int]()

        for row in 0...((currentSectionCells ).count - 1) {
**ERROR HERE==>** if currentSectionCells[row]["isVisible"] as! Bool == true {
                visibleRows.append(row)
            }
        }

        visibleRowsPerSection.append(visibleRows)

        print("visibleRows \(self.visibleRowsPerSection)")
    }
}

Solution

  • As is explained in the answer you linked to, Foundation containers (NSArray, NSDictionary etc) are untyped. So, currentSelectionCells[row] returns AnyObject. Swift won't let you subscript an AnyObject since it may not be a container. It could literally be anything.

    If you look closely at the code in the AppCoda article where you have

    for row in 0...((currentSectionCells ).count - 1) {
    

    they have

    for row in 0...((currentSectionCells as! [[String: AnyObject]]).count - 1) {
    

    They have bridged currentSelectCells to a Swift array containing a Swift dictionary of String:AnyObjectso when they later say currentSelectionCells[row] they get back a dictionary [String:AnyObject]. This is subscriptable and Swift is happy.

    So, there is an easy way to convert a plist to a Swift array. It is generally best to convert Foundation containers to Swift containers at the earliest opportunity since then you get the advantages of member typing. It would have been better if they had implemented loadCellDescriptors as something like:

    var cellDescriptors: [[String:AnyObject]]!
    
    func loadCellDescriptors() {
        if let path = NSBundle.mainBundle().pathForResource("CellDescriptor", ofType: "plist") {
            let plist = NSMutableArray(contentsOfFile: path)
            cellDescriptors = plist as! [[String:AnyObject]]
            getIndicesOfVisibleRows()
            tblExpandable.reloadData()
        }
    }