Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I get a plist as a Dictionary in Swift?

Tags:

ios

swift

People also ask

What is plist in Swift?

What's a Plist? A property list, or plist, is an XML file that contains key-value data. It's easiest to compare with a dictionary in Swift, so it's a list of values associated with keys.


You can still use NSDictionaries in Swift:

For Swift 4

 var nsDictionary: NSDictionary?
 if let path = Bundle.main.path(forResource: "Config", ofType: "plist") {
    nsDictionary = NSDictionary(contentsOfFile: path)
 }

For Swift 3+

if let path = Bundle.main.path(forResource: "Config", ofType: "plist"),
   let myDict = NSDictionary(contentsOfFile: path){
    // Use your myDict here
}

And older versions of Swift

var myDict: NSDictionary?
if let path = NSBundle.mainBundle().pathForResource("Config", ofType: "plist") {
    myDict = NSDictionary(contentsOfFile: path)
}
if let dict = myDict {
    // Use your dict here
}

The NSClasses are still available and perfectly fine to use in Swift. I think they'll probably want to shift focus to swift soon, but currently the swift APIs don't have all the functionality of the core NSClasses.


This is what I do if I want to convert a .plist to a Swift dictionary:

if let path = NSBundle.mainBundle().pathForResource("Config", ofType: "plist") {
  if let dict = NSDictionary(contentsOfFile: path) as? Dictionary<String, AnyObject> {
    // use swift dictionary as normal
  }
}

Edited for Swift 2.0:

if let path = NSBundle.mainBundle().pathForResource("Config", ofType: "plist"), dict = NSDictionary(contentsOfFile: path) as? [String: AnyObject] {
    // use swift dictionary as normal
}

Edited for Swift 3.0:

if let path = Bundle.main.path(forResource: "Config", ofType: "plist"), let dict = NSDictionary(contentsOfFile: path) as? [String: AnyObject] {
        // use swift dictionary as normal
}

Swift 4.0

You can now use the Decodable protocol to Decode a .plist into a custom struct. I will go over a basic example, for more complicated .plist structures I recommend reading up on Decodable/Encodable (a good resource is here: https://benscheirman.com/2017/06/swift-json/).

First setup your struct into the format of your .plist file. For this example I will consider a .plist with a root level Dictionary and 3 entries: 1 String with key "name", 1 Int with key "age", and 1 Boolean with key "single". Here is the struct:

struct Config: Decodable {
    private enum CodingKeys: String, CodingKey {
        case name, age, single
    }

    let name: String
    let age: Int
    let single: Bool
}

Simple enough. Now the cool part. Using the PropertyListDecoder class we can easily parse the .plist file into an instantiation of this struct:

func parseConfig() -> Config {
    let url = Bundle.main.url(forResource: "Config", withExtension: "plist")!
    let data = try! Data(contentsOf: url)
    let decoder = PropertyListDecoder()
    return try! decoder.decode(Config.self, from: data)
}

Not much more code to worry about, and its all in Swift. Better yet we now have an instantiation of the Config struct that we can easily use:

let config = parseConfig()
print(config.name) 
print(config.age)
print(config.single) 

This Prints the value for the "name", "age", and "single" keys in the .plist.


In swift 3.0 Reading from Plist.

func readPropertyList() {
        var propertyListFormat =  PropertyListSerialization.PropertyListFormat.xml //Format of the Property List.
        var plistData: [String: AnyObject] = [:] //Our data
        let plistPath: String? = Bundle.main.path(forResource: "data", ofType: "plist")! //the path of the data
        let plistXML = FileManager.default.contents(atPath: plistPath!)!
        do {//convert the data to a dictionary and handle errors.
            plistData = try PropertyListSerialization.propertyList(from: plistXML, options: .mutableContainersAndLeaves, format: &propertyListFormat) as! [String:AnyObject]

        } catch {
            print("Error reading plist: \(error), format: \(propertyListFormat)")
        }
    }

Read More HOW TO USE PROPERTY LISTS (.PLIST) IN SWIFT.


This answer uses Swift native objects rather than NSDictionary.

Swift 3.0

//get the path of the plist file
guard let plistPath = Bundle.main.path(forResource: "level1", ofType: "plist") else { return }
//load the plist as data in memory
guard let plistData = FileManager.default.contents(atPath: plistPath) else { return }
//use the format of a property list (xml)
var format = PropertyListSerialization.PropertyListFormat.xml
//convert the plist data to a Swift Dictionary
guard let  plistDict = try! PropertyListSerialization.propertyList(from: plistData, options: .mutableContainersAndLeaves, format: &format) as? [String : AnyObject] else { return }
//access the values in the dictionary 
if let value = plistDict["aKey"] as? String {
  //do something with your value
  print(value)
}
//you can also use the coalesce operator to handle possible nil values
var myValue = plistDict["aKey"] ?? ""

I have been working with Swift 3.0 and wanted to contribute an answer for the updated syntax. Additionally, and possibly more importantly, I am using the PropertyListSerialization object to do the heavy lifting, which is a lot more flexible than just using the NSDictionary as it allows for an Array as the root type of the plist.

Below is a screenshot of the plist I am using. It is a little complicated, so as to show the power available, but this will work for any allowable combination of plist types.

Sample plist file As you can see I am using an Array of String:String dictionaries to store a list of website names and their corresponding URL.

I am using the PropertyListSerialization object, as mentioned above, to do the heavy lifting for me. Additionally, Swift 3.0 has become more "Swifty" so all of the object names have lost the "NS" prefix.

let path = Bundle.main().pathForResource("DefaultSiteList", ofType: "plist")!
let url = URL(fileURLWithPath: path)
let data = try! Data(contentsOf: url)
let plist = try! PropertyListSerialization.propertyList(from: data, options: .mutableContainers, format: nil)

After the above code runs plist will be of type Array<AnyObject>, but we know what type it really is so we can cast it to the correct type:

let dictArray = plist as! [[String:String]]
// [[String:String]] is equivalent to Array< Dictionary<String, String> >

And now we can access the various properties of our Array of String:String Dictionaries in a natural way. Hopefully to convert them into actual strongly typed structs or classes ;)

print(dictArray[0]["Name"])