Xcode 8.3.3 Swift Deleting Dictionary from PLIST Array

I have created a PLIST in the following format:


<?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">
<dict>
<key>radiodata</key>
<array>
  <dict>
     <key>make</key>
       <string>Zenith</string>
     <key>model</key>
       <string>X334</string>
     <key>sn</key>
       <string>12345</string>
   </dict>
   <dict>
     <key>make</key>
       <string>RCA</string>
     <key>model</key>
       <string>XRS</string>
     <key>sn</key>
       <string>54321</string>
    </dict>
    <dict>
      <key>make</key>
        <string>Trav-Ler</string>
      <key>model</key>
        <string>H1X</string>
      <key>sn</key>
        <string>5554</string>
      </dict>
    </array>
   </dict>
</plist>


The PLIST, named RadioData.plist, resides in the Documents folder of my app.


I am able to display the data in a TableView from RadioData.plist without issue.


I have attempted to use the following code to remove a dictionsary from the plist array. It does not work.


I am seeking assistance in determining the error in my code.


I tried to use the following forKey format because each dictonary is labeled "Item 0", "Item 1"....ect


forKey: "Item " + String(indexPath.row)


override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
        if editingStyle == .delete {
            radios.remove(at: indexPath.row)
            tableView.deleteRows(at: [indexPath], with: .fade)
            /
            let urls = FileManager.default.urls(
                for: .documentDirectory, in: .userDomainMask)
            let path = urls.first!.appendingPathComponent("RadioList.plist").path
            radioInfo?.removeObject(forKey: "Item " + String(indexPath.row))
            radioInfo?.write(toFile: path, atomically:true)
      
        } else if editingStyle == .insert {
            /
        }

Mayvbe some general help here (hint: NSMutableArray):


https://stackoverflow.com/questions/28888464/how-to-modify-plist-in-swift

KMT has suggested one option (which is to do everything in ‘Objective-C space’ using mutable property list containers (

NSMutableArray
and
NSMutableDictionary
). I’m going to propose an alternative solution, which is to have your table view controller work in ‘Swift space’ and deal with the property list as a serialisation issue.

The high-level strategy here is to define a set of model types that represent the data you’re working on. For example, a radio might be represented like this:

struct Radio {
    var make: String
    var model: String
    var serialNumber: String
}

Your view controller can then have a property that is an array of this

Radio
type and manipulate that at will.

The next step is reading and writing this array to a property list. Swift 4 makes this very clean: you flag your

Radio
type as being
Codable
and Foundation’s
PropertyListEncoder
takes care of the details:
struct Radio : Codable {
    … as above …
}

var radios: [Radio] = [
    Radio(make: "Zenith", model: "X334", serialNumber: "12345"),
    Radio(make: "RCA", model: "XRS", serialNumber: "54321")
]

let encoder = PropertyListEncoder()
encoder.outputFormat = .xml
let o = try! encoder.encode(radios)

// Note: Converting a property list to a string is generally not a 
// good idea but I’m only doing it debugging purposes here.

print(String(bytes: o, encoding: .utf8)!)

This outputs the following:

…
<plist version="1.0">
<array>
    <dict>
        <key>make</key>
        <string>Zenith</string>
        <key>model</key>
        <string>X334</string>
        <key>serialNumber</key>
        <string>12345</string>
    </dict>
    …
</array>
</plist>

The only gotcha here is that the property list contains

serialNumber
, not
sn
. You can fix this with
CodingKeys
.
struct Radio : Codable {
    … as above …
    enum CodingKeys : String, CodingKey {
        case make
        case model
        case serialNumber = "sn"
    }
}

… as above …

The property list will then look like this:

…
<plist version="1.0">
<array>
    <dict>
        <key>make</key>
        <string>Zenith</string>
        <key>model</key>
        <string>X334</string>
        <key>sn</key>
        <string>12345</string>
    </dict>
    …
</array>
</plist>

Note that

Codable
includes both encoding (shown above) and decoding: you can read a property list will equally clean code using
PropertyListDecoder
.

Even if you’re not prepared to move to Swift 4 yet (and I’d encourage that, unless you’re on the bomb run to shipping a product), this is still a valid approach in Swift 3. You have to write code to manually serialise and deserialise your model types to property list types. That’s a bit of a pain but it’s more than made up for by the fact that the rest of your app works in nice Swift types rather than dealing with property list types (which are weakly typed, and thus require lots of manual type and nil checks).

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"
Xcode 8.3.3 Swift Deleting Dictionary from PLIST Array
 
 
Q