Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Read plist into specific type

When using this:

let cities: [[String : String]] = {
    guard let URL = Bundle.main.url(forResource: "cities", withExtension: "plist") else {
        return []
    }
    return NSArray(contentsOf: URL) as! [[String : String]]
}()

from this:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<array>
    <dict>
        <key>name</key>
        <string>Allentown, PA</string>
        <key>icao</key>
        <string>ABE</string>
    </dict>
    ....
</array>
</plist>

it is easy to later access the values using:

    // inside tableView(_ cellForRowAt:)
    city = cities[indexPath.row]
    cell.icaoLabel?.text = city["icao"]
    cell.nameLabel?.text = city["name"]

However, when declaring:

class City {
    var icao: String
    var name: String

    // is this init() necessary?
    init(icao: String, name: String) {
        self.icao = icao
        self.name = name
    }
}

and:

let cities: [City] = {
    guard let URL = Bundle.main.url(forResource: "cities", withExtension: "plist") else {
        return []
    }
    // ** No longer works
    return NSArray(contentsOf: URL) as! [City]
    // ** Doesn't work either
    return NSArray(contentsOf: URL)
}()

Ultimately, I want to use:

    // inside tableView(_cellForRowAt:)
    city = cities[indexPath.row]
    cell icaoLabel?.text = city.icao
    cell nameLabel?.text = city.name

How is this accomplished? Can the plist be imported this way?

Edit to the City class:

class City {
    var icao: String?
    var name: String?

    init(icao: String, name: String) {
        self.icao = icao
        self.name = name
    }

    static func loadCities() -> [City] {
        guard let URL = Bundle.main.url(forResource: "cities", withExtension: "plist") else {
            return []
        }
        let tempCities = NSArray(contentsOf: URL) as! [[String : String]]

        // now, create the desired cities array
        var cities = [City]()
        for city in tempCities {
            guard let icaoTemp = city["icao"], let nameTemp = city["name"] else {
                break
            }
            cities.append(City(icao: icaoTemp, name: nameTemp))
        }

        return cities
    }
}

accessing the values:

    let city: City = cities[indexPath.row]
    cell.icaoLabel?.text = city.icao
    cell.airportNameLabel?.text = city.name
like image 695
David Avatar asked Nov 29 '25 16:11

David


1 Answers

No, there is no built in way to convert the plist data directly into an array of your City objects. You need to do that yourself.

Load the plist just as you are now. Then iterate that array getting each little dictionary. Either extract the two values and create a City instance using your current init or add a new init that takes a dictionary and let the City class set itself up.

In the end you build a new array with each of those City instances you create as you iterate the original plist array.

It probably makes sense to wrap all of that functionality up into a Cities class that knows how to read and write the plist file and work with the list of City instances. You may end up adding other appropriate logic to the class to work with the list of cities.

like image 98
rmaddy Avatar answered Dec 02 '25 07:12

rmaddy



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!