Managing nested plists

I have a complex plist structure containing an array of dictionaries, which contain an array of dictionaries themselves:

<?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>carName</key>
        <string>Ford</string>
        <key>carAge</key>
        <integer>3</integer>
        <key>spareParts</key>
        <array>
            <dict>
                <key>carName</key>
                <string>Ford</string>
                <key>partType</key>
                <string>Door</string>
                <key>color</key>
                <string>Red</string>
                <key>age</key>
                <string>7</string>
                <key>quantity</key>
                <string>16</string>
            </dict>
            <dict>
                <key>carName</key>
                <string>Ford</string>
                <key>partType</key>
                <string>Door</string>
                <key>color</key>
                <string>Red</string>
                <key>age</key>
                <string>1</string>
                <key>quantity</key>
                <string>5</string>
            </dict>
            <dict>
                <key>carName</key>
                <string>Ford</string>
                <key>partType</key>
                <string>Door</string>
                <key>color</key>
                <string>Yellow</string>
                <key>age</key>
                <string>9</string>
                <key>quantity</key>
                <string>1</string>
            </dict>
            <dict>
                <key>carName</key>
                <string>Ford</string>
                <key>partType</key>
                <string>Door</string>
                <key>color</key>
                <string>Green</string>
                <key>age</key>
                <string>11</string>
                <key>quantity</key>
                <string>16</string>
            </dict>
            <dict>
                <key>carName</key>
                <string>Ford</string>
                <key>partType</key>
                <string>Door</string>
                <key>color</key>
                <string>Red</string>
                <key>age</key>
                <string>3</string>
                <key>quantity</key>
                <string>2</string>
            </dict>
        </array>
    </dict>
</array>
</plist>

The data is structured so that the car models are separated into dictionaries, with their corresponding parts then in an array of dictionaries further, respectively.

Working with the data I am able to locate the car model to access the array of "parts" dictionaries, however it gets more complicated as they need to match against at least two conditions to be confirmed. As each of the "parts" dictionaries is read the confirmed dictionaries are collated to reveal the total quantity, this final tally is set against what the full conditions are: e.g. there would be x23 red doors for example.

func confirmCarPart(forCarNeeded carNeeded: String, forPartNeeded partNeeded: String, forQuantityNeeded qtyNeeded: Int) -> Bool {

    let plist: String = "plistCarPartsDatabse"
    let plistInventory: String = "partsInventory"
    var confirmed: Bool = false
    var partsArray: [Any] = []
    
    createDictionary(fromPlistWithName: plist) { (result, error) in
    
    guard let editDict = result else { return }
        
        //navigate the plist to the inventory data
        let plistOutputData = Dictionary<String, Any>(_immutableCocoaDictionary: editDict)
        let plistInventoryData = plistOutputData[plistInventory] as? [[String : Any]] ?? [[:]]
        
        //confirm the car model
        for carModels in plistInventoryData {
            for model in carModels {
                if model.key == "carName" {
                    if model.value as? String == carNeeded {
                        partsArray = carParts["spareParts"] as? [[String : Any]] ?? [[:]]   //pass through to next check
                        
    
                    //confirm part type
                    for parts in partsArray as? [[String : Any]] ?? [[:]] {
                        for part in parts {
                            if part.key == "partType" && part.value as? String == partNeeded {
                                        confirmed = true
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
        return confirmed
    }

The above PoD will continue for a lot longer before I even begin to cycle through all of the parts dictionaries inside the second nested array, and all I have here is a single check for one metric, where as I need to check both "partType" and "quantity" to satisfy requirements.

Not only that but it's only a check, confirmation is one thing, but then I have to decrease/increase the quantity of the parts, and introduce new parts, which is quite daunting.

I can make the code work, but it's going to be a chore, especially addition and subtraction, so if anybody has any advice that would be well appreciated thank you.

Use a database for this: sqlite or Apple's Core Data or something else.

plists support the Codable protocol. Define your own structs that map to the plist dictionary structure and use the PropertyListDecoder to decode your plists to these structs. Decoding the plist for each query like your code seems to be showing doesn't seem very performant.

Using KVC through the value(forKey: API might be a better way to pull out bits of data from your plist. Not sure if swift key paths can also work for this.

1 Like

You're quite right, I had thought about this before. In fact I do already encode to objects from my plist already, just not to write back to the plist. It's been a lot easier this way, fortunately I don't have to use Core Data, but I will need to learn at some point.

Thanks !