NSDictionary firstIndex 标识Swift 5

NSDictionary firstIndex identification Swift 5

我正在开发一个应用程序,我需要在 Firebase 数据库中存储国家和城市。 除了存储,我还需要检索该信息并在 pickerView 中呈现给用户。鉴于此,我需要从数据库中读取国家和城市,检查它们的索引是什么并将其设置在 pickerView 中。

国家和城市存储在 JSON

{
    "Country": [
        {
            "name": "UK",
            "cities": [
                {
                    "name": "London"
                },
                {
                    "name": "Manchester"
                },
                {
                    "name": "Bristol"
                }
            ]
        },
    {
        "name": "USA",
        "cities": [
            {
                "name": "New York"
            },
            {
                "name": "Chicago"
            }
        ]
    },
    {
        "name": "China",
        "cities": [
            {
                "name": "Beijing"
            },
            {
                "name": "Shanghai"
            },
            {
                "name": "Shenzhen"
            },
            {
                "name": "Hong Kong"
            }
       ]
    }
    ]
}

我要阅读的代码JSON是

// Declared in Class
var countryList = [NSDictionary]()
var selectedRow = [NSDictionary]()
var selectedCity = ""
var selectedCountry = ""

func readJson() {
   if let path = Bundle.main.path(forResource: "Countries", ofType: "json") {
        do {
            let data = try Data(contentsOf: URL(fileURLWithPath: path), options: .mappedIfSafe)
            let jsonResult = try JSONSerialization.jsonObject(with: data, options: .mutableLeaves)
            if let jsonResult = jsonResult as? Dictionary<String, AnyObject>, let country = jsonResult["Country"] as? [NSDictionary] {

                //handles the array of countries on your json file.
                self.countryList = country
                self.selectedRow = self.countryList.first?.object(forKey: "cities") as! [NSDictionary]


            }
        } catch {
            print("error loading countries")
            // handle error

        }
    }
}

上面的代码允许我为 UIPickerView 提供 2 个会话以及国家和该国家内的城市列表。从那里,我还可以确定选择了哪个国家和城市。

作为我代码的一部分,我有一个函数可以让我识别 UIPickerView 中保存的 Country(countryIndex) 和 City(cityIndex) 的索引,以便我可以设置它,这就是我的问题开始的地方

func indexOf(city: String, inCountry country: String, in countries: [Country]) -> (countryIndex: Int, cityIndex: Int)? {
    // countries is an array of [Country]
    // var countries = [Country]()
    guard let countryIndex = countries.firstIndex(where: {[=13=].name == country}), let cityIndex = countries[countryIndex].cities.firstIndex(where: {[=13=].name == city}) else {return nil}
    //let cityIndex = 0
    return (countryIndex, cityIndex)
} // courtesy of @flanker

当我的国家和城市存储到 [国家] 但不适用于来自 JSON.

的 NSDictionary 时,此功能运行良好

我试过了 1) 通过 [NSDictionary] 更改 [Country],通过 "countryList" 更改 "countries",通过 "Country" 更改 "name" 在这里我收到错误 "NSDictionary has no member Country" 我还试图只留下 $0 == Country ,但效果不佳。 2) 也试过 "countryList.firstIndex(of: "USA")" 但出现以下错误 无法将类型 'String' 的值转换为预期的参数类型 'NSDictionary'

有人可以提供帮助吗?我怎样才能让 func indexOf 再次工作?

谢谢

根据@vadian 的建议更新

我的更新代码是

import UIKit

class ViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource {


    @IBOutlet weak var pickerView: UIPickerView!
    @IBOutlet weak var countryLbl: UILabel!

    var familyNames: [String] = []
    var fontName = "Arial"
    let fontCount = 0
    var countryList = [Country]()
    var selectedRow = [City]()
    var selectedCity : City?
    var selectedCountry : Country?

    struct Root : Decodable {
        let country : [Country] // better plural let countries
        private enum CodingKeys : String, CodingKey { case country  = "Country" }
    }

    struct Country : Decodable {
        var name : String
        var cities : [City]
    }

    struct City : Decodable {
        var name : String
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        pickerView.delegate = self
        pickerView.dataSource = self

        fontName = "HelveticaNeue" 
    }

func indexOf(city: String, inCountry country: String, in countries: [Country]) -> (countryIndex: Int, cityIndex: Int)? {
    guard let countryIndex = countries.firstIndex(where: {[=14=].name == country}), let cityIndex = countries[countryIndex].cities.firstIndex(where: {[=14=].name == city}) else {return nil}
    return (countryIndex, cityIndex)
}

    func pickerView(_ pickerView: UIPickerView, widthForComponent component: Int) -> CGFloat {
        if component == 0 {
            return 80
        } else {
            return 300
        }

    }

    func numberOfComponents(in pickerView: UIPickerView) -> Int {
        return 2
    }

    func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
        if component == 0 {
            return countryList.count
        } else {
            return selectedRow.count
        }
    }

    func pickerView(_ pickerView: UIPickerView, viewForRow row: Int, forComponent component: Int, reusing view: UIView?) -> UIView {

        var rowTitle = ""
        let pickerLabel = UILabel()

        pickerLabel.textColor = UIColor.blue

        switch component {
        case 0:
            rowTitle = countryList[row].name
        case 1:
            rowTitle = selectedRow[row].name
        default:
            break
        }

        pickerLabel.text = rowTitle
        pickerLabel.font = UIFont(name: fontName, size: 20.0)
        pickerLabel.textAlignment = .center

        return pickerLabel
    }

func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
    pickerView.reloadAllComponents()

    if component == 0 {
        self.selectedCountry = self.countryList[row]
        self.selectedRow = self.countryList[row].cities

        pickerView.reloadComponent(1)
        self.pickerView.selectRow(0, inComponent: 1, animated: true)
        self.selectedCity = self.selectedRow[0]
    } else {
        self.selectedCity = self.selectedRow[row]
    }

    if let indexes = indexOf(city: self.selectedCity!.name, inCountry: self.selectedCountry!.name, in: countryList) {
           //do something with indexes.countryIndex and indexes.cityIndex
           print("This is the result \(indexes.cityIndex) and \(indexes.countryIndex)")

           }
    countryLbl.text = "The right answer is: \(self.selectedCountry?.name) and the city is \(self.selectedCity?.name)"
    }

    func readJson() {
        let url = Bundle.main.url(forResource: "Countries", withExtension: "json")!
        do {
            let data = try Data(contentsOf: url)
            let jsonResult = try JSONDecoder().decode(Root.self, from: data)

            //handles the array of countries on your json file.
            self.countryList = jsonResult.country
            self.selectedRow = self.countryList.first!.cities
        } catch {
            print("error loading countries", error)
        }
    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(true)
        readJson()
    }
}

您还必须将捆绑包中的 JSON 解码为自定义结构

struct Root : Decodable {
    let country : [Country] // better plural let countries
    private enum CodingKeys : String, CodingKey { case country  = "Country" }
}

struct Country : Decodable {
    let name : String
    let cities : [City]
}

struct City : Decodable {
    let name : String
}

var countryList = [Country]()
var selectedRow : [City]()
var selectedCity : City?
var selectedCountry : Country?

func readJson() {
    let url = Bundle.main.url(forResource: "Countries", withExtension: "json")!
    do {
        let data = try Data(contentsOf: url)
        let jsonResult = try JSONDecoder().decode(Root.self, from: data)

        //handles the array of countries on your json file.
        self.countryList = jsonResult.country
        self.selectedRow = self.countryList.first!.cities
    } catch {
        print("error loading countries", error)
    }
}

那么你的方法 indexOf(city:inCountry:in:) 有效

由于文件在应用程序包中,考虑省略根字典 "Country" 并解码 [Country].self


通常的旁注:

  • 不要在 Swift 中使用 NS... 集合类型。你扔掉了类型信息。使用本机类型。

  • .mutableContainers.mutableLeaves 在 Swift 中毫无意义。除此之外,具有讽刺意味的是,您还是将值分配给了 im 可变常量。

  • A JSON字典在Swift 3+总是值类型[String:Any]不是引用类型[String:AnyObject].