How to Use Property Lists (.plist) in Swift

This will be the first in a series of lessons on persistence in iOS applications. Persistence is keep data around even if your app ends. As we’ll see in this series there are many techniques to save and read data. One Common one Apple uses frequently in Xcode is the Property list, or plist, named for its extension. In this first lesson we’ll look at how to make, modify and read property lists, and when to use them.

Understanding Property Lists

Open a new project in Xcode with a Single View template using Swift as the language. Save the project as SwiftPropertyListDemo. In the Project inspector, you will find a file info.plist. Select this file.

2016-02-10_05-52-25

You will find a screen looking like this:

2016-02-10_05-53-23

Many of the settings about your app the compiler needs to know is here. That’s one of the uses for property lists: to keep settings between runs of your application. Right click on the info.plist file name. In the menu that appears select Open as>Source Code

2016-02-10_06-01-03

You will get a very different looking screen of XML code starting like 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">
<dict>
	<key>CFBundleDevelopmentRegion</key>
	<string>en</string>
	<key>CFBundleExecutable</key>
	<string>$(EXECUTABLE_NAME)</string>
	<key>CFBundleIdentifier</key>
	<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
	<key>CFBundleInfoDictionaryVersion</key>
	<string>6.0</string>
	<key>CFBundleName</key>
	<string>$(PRODUCT_NAME)</string>
	<key>CFBundlePackageType</key>
	<string>APPL</string>
	<key>CFBundleShortVersionString</key>
	<string>1.0</string>

In most cases, a .plist file is a XML file stored as a dictionary. We can edit property lists either in the property list editor or in the plists. You’ll notice there are key tags and string tags and one dict tag. This makes a dictionary of [String:String]

Understanding Basic XML

If that didn’t make sense to you, Lets discuss some basic XML. XML like HTML is a text file with start and end tags indicated by the characters < and > with an ending indicator of /. Unlike HTML, there are no reserved tags. The author makes up their own. In some program those tags indicate data. You’ll find XML in things like RSS feeds and podcast metadata. As we’ll discuss in a later lesson, some databases can be XML files. Microsoft Word’s .xdoc file format is actually a XML document.

All XML documents start with a header stating they are a XML document and what kind of document. In the above property list and all property lists we have the following header:

<?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">

In a property list, we have the following tags available

property list XML tags

Apple’s internal property list is a dictionary with key:value pairings. You can see here most of the entries are of the form of a key and a string, but there are Bool and array as well:

<dict>
	<key>CFBundleDevelopmentRegion</key>
	<string>en</string>
...
        <key>LSRequiresIPhoneOS</key>
	<true/>
...
        <key>UISupportedInterfaceOrientations</key>
	<array>
		<string>UIInterfaceOrientationPortrait</string>
		<string>UIInterfaceOrientationLandscapeLeft</string>
		<string>UIInterfaceOrientationLandscapeRight</string>
	</array>

</dict>

Making Your Own Property Lists

You can make your own property lists. Within Xcode, there is a file template. Press Command-N to get a new file. Under iOS Resource select Property list

2016-02-10_07-06-42

Name the property list data, and store it in the SwiftPropertyListDemo group folder. You will see a blank property list in a dictionary.

2016-02-10_07-10-41

Hover over the root entry and you will find a + button to add an entry

2016-02-10_07-13-00

Click the button and a new entry appears, with a default type of String

2016-02-10_07-15-25

Click the type selector indicating String, and you will find the following selections

2016-02-10_07-15-26

Select Number. Change the value to 0.3, and the key to Red

2016-02-10_07-15-27

Press root’s add button two more times and add a Green value of 0 and Blue as a blank string.

2016-02-11_06-55-24

We’ll use these three values to save a color. While there is a better way to save colors that will be covered in another lesson, this is a simpler way for us to set a color from a property list.

Click the + button on Root. Make an entry with the key Pies and a type array. You’ll get an arrow next to the key Pie . Press the + button on the Pies key. You’ll get another entry.

2016-02-11_07-06-39

The + on any entry makes another entry below it. Since we do not want this entry, click the on the key to delete it. Notice the triangle next to Pies click it so it points down. In order to get elements in a dictionary or array you must click this to point down. Click the + on the pie entry four times to get four elements to your array.

2016-02-11_07-11-58

Keeping the array elements as strings add four pies to the property list.

2016-02-11_07-14-08

Click the Root + button. Add a boolean entry with the key Inverse Colors set to NO.

You can re-arrange your items by click and dragging up and down. You’ll see a blue line indicating where the item will be inserted. Re-arrange the keys like this. As far as the compiler is concerned you don’t have to do this, but it is easier for humans to read and understand.

2016-02-11_07-20-32

Editing the XML Property List

Let’s look at the XML for our property list. Right Click data.plist and select Open As>Source Code. We get the following code

<?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>Inverse Color</key>
	<false/>
	<key>Red</key>
	<real>0.3</real>
	<key>Green</key>
	<key>Green</key>
	<integer>0</integer>
	<key>Blue</key>
	<string></string>
	<key>Pies</key>
	<array>
		<string>Cherry</string>
		<string>Apple</string>
		<string>French Silk</string>
		<string>Coconut Cream</string>
	</array>
</dict>
</plist>

Green is an integer and Blue is a string. The editor assumes an integer if you do not include a decimal point. Let’s change both real and add a value of 0.3 to the Blue entry. Change this

<key>Green</key>
<integer>0</integer>
<key>Blue</key>
<string></string>

to this

<key>Green</key>
<real>0.0</real>
<key>Blue</key>
<real>0.3</real>

Let’s add two more pies to our array, under the entry for Coconut Cream add the following:

<string>Blueberry</string>
<string>Boston Cream</string>

Switch back to the editor by selecting Open As> Property list and you will see your changes to the property list.

2016-02-11_07-55-22

Setting up a Demo Application

Let’s set up a simple table view application to use our new property list. If you are not familiar wth table view controllers I’d suggest reading this first. Go to the story board and delete the current view controller. Drag a table view controller to the storyboard. In the attribute inspector, click on Is Initial View Controller. Select the table view cell and change the Style to Basic and the Identifier to cell.

2016-02-11_07-26-20

Now go to the View Controller. Change the view controller to subclass UITableViewController instead of View Controller

class ViewController: UITableViewController {

Go back to the storyboard and select the table view controller. In the Identity inspector, set the Class to View Controller.

Go to your view controller code. Add the following properties with some test data:

var isColorInverted = true
var color1 = UIColor(
    red: 0.3,
    green: 0.3,
    blue: 0.0,
    alpha: 1.0)
let color2 = UIColor(
    white: 0.9,
    alpha: 1.0)
var pies = [
    "Pizza",
    "Chicken Pot",
     "Shepherd's"
]    

Add the delegates and data sources. At the bottom of the class add this:

 //MARK: - Table View Data sources and Delegates
    override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
        return 1
    }
    override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return pies.count
    }
    override func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
        return "Pies"
    }
    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath)
        let row = indexPath.row
        cell.textLabel?.text = pies[row]
        if isColorInverted { //invert colors by flag status
            cell.backgroundColor = color2
            cell.textLabel?.textColor = color1
        }else{
            cell.backgroundColor = color1
            cell.textLabel?.textColor = color2
        }
        return cell
        
    }


We’ll display the value of the array in the cells and change the color of the table view cell depending on the value of isColorInverted. Now build and run this to make sure the app works with the test data.

2016-02-11_07-29-52

Reading a .plist in Swift

Add the following above the viewDidLoad method.

func readPropertyList(){
    
}

We’re going to make this a function that we’ll call in our viewDidLoad to set the properties for the class according to the property list. Add the following property to the new function:

var format = NSPropertyListFormat.XMLFormat_v1_0 //format of the property list

The variable format is the format of the property list. While XML is the most common there are a few other types available all listed in the enumeration NSPropertyListFormat. This will be used in a parameter later on, but needs an initial value. The method we’ll run later may change format to a different format during the conversion process so it will be a pass through variable. Add under this property

var plistData:[String:AnyObject] = [:]  //our data

We’ll use plistData to store the dictionary entries of the property list. I’m making an assumption here you may not want to make. For almost all cases a property list will be a dictionary, so I’m setting this up for a dictionary. That might be a bad assumption and you can alternately write var plistData:AnyObject! = nil. or whatever format you are expecting from the property list. Now add this line

let plistPath:String? = NSBundle.mainBundle().pathForResource("data", ofType: "plist")!

This finds and returns the path of our list from the main bundle. I’m assuming it is in the main bundle here or there is only one data.plist in the bundle. Both could cause errors. For simplicity I made sure it was. You should add more robust code to find it where it may be hiding or if there are duplicates. Now add this

 let plistXML = NSFileManager.defaultManager().contentsAtPath(plistPath!)!

The constant plistXML is the XML data as type NSData format. The function may return nil if the file is not found. Now add this under it

do{
plistData = try NSPropertyListSerialization.propertyListWithData(plistXML,
    options: .MutableContainersAndLeaves, 
    format: &format) 
    as! [String:AnyObject]
}
catch{
    print("Error reading plist: \(error), format: \(format)")
}        

Here we convert the XML data to a dictionary. There is a class, NSPropertyListSerialization which along with the class method propertyListWithData does the conversions. It contains several parameters to discuss. The first parameter is the data we will convert with is of Type NSData, which we will discuss in more detail in the next lesson in this series. The second parameter is a constant from the enum NSPropertyListMutabilityOptions. This parameter has three possible options which affect the mutability of the dictionary created. It’s a feature for Objective-C, not Swift. In Swift it’s meaningless. The parameter format is a pass through variable, so needs the &format. If we did not use it in the print statement, we could also put

While I’ve been fast and loose with error checking for this demo, this is the one place I’m careful. If you edit your plist in XML on a text editor, you have the chance of introducing syntax errors which will mess up everything. For that reason, I use Swift’s error control do...try...catch clause to catch syntax errors in the XML.

If this step is successful, we have in plistData a dictionary of our property list. Add the following code to assign the Bool value in the do block

isColorInverted = plistData["Inverse Color"] as! Bool

The value of dictionary is of type AnyObject. For each value we need to downcast it to assign it properly. For isColorInverted, that is a Bool. For the XML tags real and integer, it can be any real or integer type. Add this code :

let red = plistData["Red"] as! CGFloat
let green = plistData["Green"] as! CGFloat
let blue = plistData["Blue"] as! CGFloat
color1 = UIColor(red: red, green: green, blue: blue, alpha: 1.0)

We did not have to downcast to Float and then convert to a CGFloat. We can Downcast directly to CGFloat. Add this to assign the array pie

pies = plistData["Pies"] as! [String]

The function is now complete and will assign to the properties of the class values from the the property list. Change viewDidLoad to use it.

override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        readPropertyList()
    }

Build and run. You will get different results than your first run

2016-02-11_07-42-07

When to Use plists

I’m conservative with using plists. While you can write plists, I never do. I’m of the thought that property lists should be a read-only at run time. While there are several reasons for that, my biggest is there are much better ways to save data than plists. I use them for two things only. Instead of setting global constants, I’ll make a plist and load it into the classes that need it. A small data file is a lot easier to change than finding a constant in a mass of code. Secondly, I use it as I did here to load tables and pickers with static data. Instead of making a huge amount of code to set arrays, I make it an external file. These both are read-only operations. In the lessons the follow, we’ll look at some of the ways to both read and write data.

If you liked this lesson and would like to be notified about more lessons, subscribe to the Make App Pie NewsletterWhich sends a new iOS lesson every Thursday and news and sneak previews of upcoming works every Monday

The Whole Code

ViewController.swift

//
//  ViewController.swift
//  SwiftPropertyListDemo
//
//  Created by Steven Lipton on 2/10/16.
//  Copyright © 2016 MakeAppPie.Com. All rights reserved.
//

import UIKit

class ViewController: UITableViewController {
    var isColorInverted = true
    var color1 = UIColor(red: 0.3, green: 0.3, blue: 0.0, alpha: 1.0)
    let color2 = UIColor(white: 0.9, alpha: 1.0)
    var pies = ["Pizza","Chicken Pot","Shepherd's"]
    
    func readPropertyList(){
        
        var format = NSPropertyListFormat.XMLFormat_v1_0 //format of the property list
        var plistData:[String:AnyObject] = [:]  //our data
        let plistPath:String? = NSBundle.mainBundle().pathForResource("data", ofType: "plist")! //the path of the data
        let plistXML = NSFileManager.defaultManager().contentsAtPath(plistPath!)! //the data in XML format
        do{ //convert the data to a dictionary and handle errors.
            plistData = try NSPropertyListSerialization.propertyListWithData(plistXML,options: .MutableContainersAndLeaves,format: &format)as! [String:AnyObject]
            
            //assign the values in the dictionary to the properties
            isColorInverted = plistData["Inverse Color"] as! Bool
            
            let red = plistData["Red"] as! CGFloat
            let green = plistData["Green"] as! CGFloat
            let blue = plistData["Blue"] as! CGFloat
            color1 = UIColor(red: red, green: green, blue: blue, alpha: 1.0)
            
            pies = plistData["Pies"] as! [String]
        }
        catch{ // error condition
            print("Error reading plist: \(error), format: \(format)")
        }
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        readPropertyList()
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
    //MARK: - Table View Data sources and Delegates
    override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
        return 1
    }
    override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return pies.count
    }
    override func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
        return "Pies"
    }
    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath)
        let row = indexPath.row
        cell.textLabel?.text = pies[row]
        if isColorInverted { //invert colors by flag status
            cell.backgroundColor = color2
            cell.textLabel?.textColor = color1
        }else{
            cell.backgroundColor = color1
            cell.textLabel?.textColor = color2
        }
        return cell
        
    }

}


data.plist

<?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>Inverse Color</key>
	<false/>
	<key>Red</key>
	<real>0.3</real>
	<key>Green</key>
	<real>0</real>
	<key>Blue</key>
	<real>0.3</real>
	<key>Pies</key>
	<array>
		<string>Cherry</string>
		<string>Apple</string>
		<string>French Silk</string>
		<string>Coconut Cream</string>
		<string>Blueberry</string>
		<string>Boston Cream</string>
	</array>
</dict>
</plist>

16 Replies to “How to Use Property Lists (.plist) in Swift”

  1. The tutorials here contain a good overview and working code, but sometimes not a lot of developer insights material or explanation of the parameters passed and how choosing one of the other might affect the code:

    “MutableContainersAndLeaves”

    What would the reader take from that option? What are the alternatives and why would we choose them? What could go wrong by doing that?

    1. Goffredo,
      For your question about MutableContainersAndLeaves, I have added a few words about it in the post. I’m surprised that with your knowledge you didn’t see the obvious answer that the options: parameter is essentially meaningless, since Swift does not set mutability by class as Objective-C does. It’s only there because of Objective-C. Not to mention what ARC will do to plistData the way the program is written.

      I do not write exhaustively on a subject. My audience wants an answer they can use immediately. The developer insights I do put in, such as my last paragraph of plist best being read only, or the XML tag chart which I have found no where else (Apple’s is outdated by four years) are ther to be useful to those who have no idea these things exist. When I hit I believe irrelevant to getting the job done, I may skip it as I did with the options: parameter.

      I strongly encourage you to get those “developer insights” out there. Get yourself a free wordpress.com site and start writing. You seem good at curating a few big names or commenting on others, but I think you need to get your own ideas out there. I look forward to reading your insights.

  2. Steve,

    I apologise if my original comment appeared rude.
    I was not commenting about what I understood or not in your post not commenting about the quality of your content or your books (like the auto layout one I already read which has the same philosophy as you mention in your response), but I wanted to highlight what I Like and think other people might benefit too from this such technical pieces. I do have your work in my Feedly list as I recognise its value, not good at expressing that.

    It is what makes the iOS By Tutorials series so useful as reference or the TIJ one valuable. All that goes beyond what the API’s are and their official docs, what comes from the experience of the developers having written that code 10 times already. I am not demanding a lunch for free. It is definitely something I ought to do myself as it is a kind of duty to help others.

    1. No problem. Thank you and I too apologize for not understanding the context. I’ll still urge you to get your own blog/website though. You have a lot of knowledge to share. It would be a shame to waste it. You seem to have a strength in curation more than original writing. Curate others with some original insight of your own. just a thought.

      Steve

  3. In the app I’m programming I’m trying to add data in the following format: Int; Name; Definition; [Int]. I tried doing so in an array of arrays like so: “let thing = [[name],[definition],[1,2,3,7]]”. This caused the app to stall out during compile after about 8 entries (I need to make about 100).

    Is the property list the appropriate place for this type of data? It is accessed regularly while running the app (but nothing is ever added). I’m trying to avoid doing all the data entry only to discover I’ve put it in the wrong spot.

    Love this series of tutorials, I’m hoping they continue through to show when to use core data or other solutions!

    1. Oops – there was actually a whole additional layer, it should have been:
      “let thing = [ [[name],[definition],[1,2,3]], [[name2],[definition2],[3,4,5]] ]” etc. No wonder Xcode broke, my head hurts just looking at that.

    2. We will be working our way towards core data, yes.

      I wouldn’t put that kind of data in a property list. On the most basic level you could use a .csv text file, which we’ll be discussing late April or Early May, depending if I can get three other things out of the way first (Custom presentation controllers, the two last WatchOS posts and a few podcast recordings). I’ll have better scheduling notes in the newsletter for that on Monday.

  4. You are being asked to login because ashokicreate@gmail.com is used by an account you are not logged into now.

    By logging in you’ll post the following comment to How to Use Property Lists (.plist) in Swift:
    Hi Steven thanks for tutorial. it’s really helpful.

    I am new to ios programming and i have few queries.

    As i can see our ultimate goal is to read data from plist as a NSDictionary.
    then we can just make use of below code right?

    let path = NSBundle.mainBundle().pathForResource(“Config”, ofType: “plist”)
    myDict = NSDictionary(contentsOfFile: path)

    Which approach is best?

    1. You could do that, it’s more Objective-C than I’d want in my Swift code,but…. I’m not clear the results will always be what you want. You still need to do a lot of error handling, which is what most of that code is. The problems are things beyond your control as a developer: is the XML file well formed and what form is it in? Does the file even exist? Those are things you can’t 100% trust the system, because they are external to your application at times. I’m being very explicit (though this is old swift 2.0 syntax) here to prevent errors which would crash the application. In your suggestion, path is an optional. If the file is not found it will crash, because path in the contensOfFile parameter is explicit see you’d have to write it like this (Swift 3.0 syntax here):

       let path = Bundle.main.path(forResource: "Config", ofType: "plist")
       let  myDict = NSDictionary(contentsOfFile: path!)
      

      if path is nil due to the file not being there or damages or timing out in reading, the app crashes.

      I prefer to do the error handling while I’m doing the task of reading. Waiting for reading the data and getting nil if it isn’t there(which would be the code you suggest) gets messy in my processing code.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s