Peter71 Peter71 - 1 year ago 60
Swift Question

Swift: pList with "complex" data

As I read and tried out :-) I can only save some simple data types on pList files. Nevertheless I like to use structs, classes etc to represent my data. This should be saved as easily as possible to a pList file and gets reloaded.

I see, that NSData is a valid type for pLists. And also, that this is a general data type. So it is a good idea to move/convert/force a struct or class variable into a NSData object to be saved and reloaded? How would that be done?

Till now I'm using something like this for saving:

let dict: NSMutableDictionary = ["XYZ": "XYZ"]

// saving values
dict.setObject(myBasicArray, forKey: "BasicArray")

dict.writeToFile(path, atomically: false)

Answer Source

There's several ways to do this, you can adhere to the NSCoding protocol or you can write methods to convert your class/struct to a Dictionary and serialize from there.

Here's a good intro to using the NSCoding protocol.

As for converting to and from a Dictionary the usual way is to provide a failable init method that takes a Dictionary<String, AnyObject> which validates and copies the key:value pairs to the member variables. You also provide a method that returns a Dictionary<String, AnyObject> with the same key:value pairs as the init takes. Then you can serialize by calling the create method and serializing the resulting Dictionary, you deserialize by reading into a Dictionary and passing that in to the init method.

Here's an example of the conversion:

struct Foo {
  let one: Int
  let two: String

  init(one:Int, two: String) { = one
    self.two = two

  init?(dict:[String: AnyObject]) {
    guard let
      one = dict["one"] as? Int,
      two = dict["two"] as? String else { return nil } = one
    self.two = two

  func toDictionary() -> [String: AnyObject] {
    var retval = [String: AnyObject]()
    if let 
      one = as? AnyObject,
      two = self.two as? AnyObject {
      retval["one"] = one
      retval["two"] = two
    return retval

Here's how to test it:

// create struct
let writeStruct = Foo(one: 1, two: "one")
print(writeStruct, "\n")

// write to plist
let writeDict = writeStruct.toDictionary() as NSDictionary
let path = ("~/test.plist" as NSString).stringByExpandingTildeInPath
writeDict.writeToFile(path, atomically: true)

// print contents of file
print(try NSString(contentsOfFile: path, encoding: NSUTF8StringEncoding))

// read plist and recreate struct
if let
  readDict = NSDictionary(contentsOfFile: path) as? [String:AnyObject],
  readStruct = Foo(dict: readDict) {


Foo(one: 1, two: "one") 

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "">
<plist version="1.0">

Foo(one: 1, two: "one")