Olli D. Olli D. - 1 month ago 15
Swift Question

Swift 3 - Sorting array/dictionary: '(AnyObject)' is not a subtype of 'NSString'

this code is a glossary for abbreviations (Table View) sorted in alphabetical order and worked fine with Swift 2.2.
But after conversion to Swift 3 there is an error '(AnyObject)' is not a subtype of 'NSString' at this line:

array_index = dict_list.allKeys.sorted { ($0 as AnyObject).localizedCaseInsensitiveCompare($1 as! String) == ComparisonResult.orderedAscending }


The code looks like this:

import UIKit

class GlosTableViewController: UITableViewController {

var array_index:NSArray = []

var total_index:NSArray = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"]

var dict_list:NSDictionary = [
"A" : ["AA1", "AA2"],
"C" : ["CA1", "CB2", "CB4"],
"D" : ["D12", "D13"],
"E" : ["EA1", "EI1", "EL1", "EL2", "EQ1", "E12"],
"F" : ["F33"],
"H" : ["H11", "H14"],
"I" : ["IM3", "IM5"],
"L" : ["LH1"],
"M" : ["M99"],
"N" : ["NE1", "NE2", "NE3", "NE4"],
"P" : ["PE1", "PE2", "PE3", "PE4", "PE5"],
"Q" : ["QT2"],
"R" : ["RC2", "RC5", "RC7",
"T" : ["TI2"],
]

var dict_list1:NSDictionary = [
"AA1" : "Dictionary text for item",
"AA2" : "Dictionary text for item",
"CA1" : "Dictionary text for item",
"CB2" : "Dictionary text for item",
"CB4" : "Dictionary text for item",
"D12." : "Dictionary text for item",
"D13" : "Dictionary text for item",
"EA1" : "Dictionary text for item",
"EI1" : "Dictionary text for item",
"EL1" : "Dictionary text for item",
"EL2" : "Dictionary text for item",
"EQ1" : "Dictionary text for item",
"E12" : "Dictionary text for item",
"F33" : "Dictionary text for item",
"H11" : "Dictionary text for item",
"H14" : "Dictionary text for item",
"IM3" : "Dictionary text for item",
"IM5" : "Dictionary text for item",
"LH1" : "Dictionary text for item",
"M99" : "Dictionary text for item",
"NE1" : "Dictionary text for item",
"NE2" : "Dictionary text for item",
"NE3" : "Dictionary text for item",
"NE4" : "Dictionary text for item",
"PE1" : "Dictionary text for item",
"PE2" : "Dictionary text for item",
"PE3" : "Dictionary text for item",
"PE4" : "Dictionary text for item",
"PE5" : "Dictionary text for item",
"QT2" : "Dictionary text for item",
"RC2" : "Dictionary text for item",
"RC5" : "Dictionary text for item",
"RC7" : "Dictionary text for item",
"TI2" : "Dictionary text for item"
]

override func viewDidLoad() {
super.viewDidLoad()

array_index = dict_list.allKeys.sorted { ($0 as AnyObject).localizedCaseInsensitiveCompare($1 as! String) == ComparisonResult.orderedAscending }

}
override func numberOfSections(in tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return array_index.count
}

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
let sectionTitle = array_index.object(at: section)
as! String
let sectionAnimals = dict_list.object(forKey: sectionTitle) as! NSArray
return sectionAnimals.count
}

override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 80
}

override func sectionIndexTitles(for tableView: UITableView) -> [String]? {
return total_index as? [String]
}

override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return array_index.object(at: section) as? String
}

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell_Glos", for: indexPath) as! GlosTableViewCell
let sectionTitle = array_index.object(at: indexPath.section) as! String
let sectionAnimals = dict_list.object(forKey: sectionTitle) as! NSArray
let animal = sectionAnimals.object(at: indexPath.row) as! String
cell.label_title.text = animal
cell.label_subtitle.text = dict_list1.object(forKey: animal) as? String

return cell
}
}


I did not found a solution in the forum. Can someone help me on this?

Answer

The short solution would be to to replace var array_index: NSArray = [] with var array_index: [Any] = [].

The nicer way would be to get rid of NSArray and NSDictionary and replace them with Swift Arrays and Dictionaries with beautiful types:

import UIKit

class GlosTableViewController: UITableViewController {

    var array_index: [String] = []

    var total_index: [String] = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K",
                                 "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V",
                                 "W", "X", "Y", "Z"]

    var dict_list: [String: [String]] = [
        "A" : ["AA1", "AA2"],
        "C" : ["CA1", "CB2", "CB4"],
        "D" : ["D12", "D13"],
        "E" : ["EA1", "EI1", "EL1", "EL2", "EQ1", "E12"],
        "F" : ["F33"],
        "H" : ["H11", "H14"],
        "I" : ["IM3", "IM5"],
        "L" : ["LH1"],
        "M" : ["M99"],
        "N" : ["NE1", "NE2", "NE3", "NE4"],
        "P" : ["PE1", "PE2", "PE3", "PE4", "PE5"],
        "Q" : ["QT2"],
        "R" : ["RC2", "RC5", "RC7"],
        "T" : ["TI2"]
    ]

    var dict_list1: [String: String] = [
        "AA1" : "Dictionary text for item",
        "AA2" : "Dictionary text for item",
        "CA1" : "Dictionary text for item",
        "CB2" : "Dictionary text for item",
        "CB4" : "Dictionary text for item",
        "D12." : "Dictionary text for item",
        "D13" : "Dictionary text for item",
        "EA1" : "Dictionary text for item",
        "EI1" : "Dictionary text for item",
        "EL1" : "Dictionary text for item",
        "EL2" : "Dictionary text for item",
        "EQ1" : "Dictionary text for item",
        "E12" : "Dictionary text for item",
        "F33" : "Dictionary text for item",
        "H11" : "Dictionary text for item",
        "H14" : "Dictionary text for item",
        "IM3" : "Dictionary text for item",
        "IM5" : "Dictionary text for item",
        "LH1" : "Dictionary text for item",
        "M99" : "Dictionary text for item",
        "NE1" : "Dictionary text for item",
        "NE2" : "Dictionary text for item",
        "NE3" : "Dictionary text for item",
        "NE4" : "Dictionary text for item",
        "PE1" : "Dictionary text for item",
        "PE2" : "Dictionary text for item",
        "PE3" : "Dictionary text for item",
        "PE4" : "Dictionary text for item",
        "PE5" : "Dictionary text for item",
        "QT2" : "Dictionary text for item",
        "RC2" : "Dictionary text for item",
        "RC5" : "Dictionary text for item",
        "RC7" : "Dictionary text for item",
        "TI2" : "Dictionary text for item"
    ]

    override func viewDidLoad() {
        super.viewDidLoad()

        self.array_index = dict_list.keys.sorted {
            $0.localizedCaseInsensitiveCompare($1) == .orderedAscending
        }
    }

    override func numberOfSections(in tableView: UITableView) -> Int {
        // #warning Incomplete implementation, return the number of sections
        return array_index.count
    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        // #warning Incomplete implementation, return the number of rows
        let sectionTitle = array_index[section]
        let sectionAnimals = dict_list[sectionTitle]
        return sectionAnimals?.count ?? 0
    }

    override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return 80
    }

    override func sectionIndexTitles(for tableView: UITableView) -> [String]? {
        return total_index
    }

    override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
        return array_index[section]
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "Cell_Glos", for: indexPath) as! GlosTableViewCell
        let sectionTitle = array_index[indexPath.section]
        let sectionAnimals = dict_list[sectionTitle]
        let animal = sectionAnimals[indexPath.row]
        cell.label_title.text = animal
        cell.label_subtitle.text = dict_list1[animal]

        return cell
    }
}