Working With Property Lists In Swift – Part 1

March 4th, 2021

⏱ Reading Time: 5 mins

A property list file, better known as a plist file, is a specific kind of file that can store data in key-value pairs. In fact, property list files are XML files simply named differently in the Apple ecosystem. Making a parallelization, plist files are similar to dictionaries in Swift; the collection type that can also store key-value pairs of data.

The best example of a property list file, is the Info.plist in every Xcode project. It contains a big number of default settings, configuration and information regarding the app, but more entries can be added on demand. For example, in order to make use of the device camera, it’s required to add the NSCameraUserDescription key, accompanied with a String value describing the purpose of using the camera.

Property list files can be proved perfect in order to store small amounts of data that can be described as key-value pairs. Just think that user default settings (NSUserDefaults) are stored in such a file in iOS apps. And it’s really easy to perform such tasks programmatically in Swift. In this post I’ll show you how to read from and write to property list files. A further discussion with some more advanced techniques is available in the second post.

A sample Plist file and a custom type

To start, suppose that we have the following property list file in the app’s bundle called DefaultShape.plist. Its purpose is to contain the default data regarding hypothetical shape types:

DefaultShape.plist

Contained values are of various data types:

  • width, height, borderWidth and cornerRadius are Numbers.
  • backgroundColor and borderColor are Strings representing colors as Hex values.
  • isInteractive is a Boolean value, indicating whether a shape will be interacting on user actions or not.
  • patternImage is a Data value and its purpose is to store the actual data of an image that can be used as a background pattern on the shape.

Say now that we have the following type that represents a custom shape. It has properties similar to the keys of the property list, and that’s important so things work almost automatically next in this post:

The goal of this post is to initialize a CustomShape instance loading the plist contents and storing them to the respective properties. Notice that all of them have initial values so we avoid errors if initializing with the plist contents fails.

Decoding a property list with Codable type

Codable is not a real protocol itself in Swift. Instead, it’s the combination of two other protocols, Encodable and Decodable. With those two, it’s easy to encode and decode a native custom Swift type to and from a specific data format respectively, such as JSON and property list.

In order to manage to load plist file contents to a CustomShape instance, it’s necessary to decode the property list to that type. To achieve that, we’ll make CustomShape conform to Codable:

Note that if you want only to decode, then instead of Codable you can adopt the Decodable protocol and avoid the additional conformance to Encodable that Codable brings along. The same applies in case you want to encode only; use Encodable and not Codable. Here I’m using Codable because I’ll show later how to encode a CustomShape instance to a property list file.

Next, let’s define the following custom initializer method that will be containing a do-catch statement. That’s because we’ll call methods that may throw an exception:

The argument it expects is a URL to a property list file. I choose to implement it that way because it’s a more general solution that can work for plist files existing in both the app bundle and other locations, such as the documents directory.

Note that just printing the potential error is not a proper error handling. In real apps a more meaningful and purposeful treatment should be implemented in the catch block.

The next step is to load the plist contents, and decode them to a CustomShape type. We manage the former by initializing a Data object using the provided URL. Note that it’s an operation marked with the try keyword as it can throw an exception:

With the plistData object handy, we can decode the plist contents to a CustomShape instance. We will initialize an object of a class called PropertyListDecoder in order to do that, and through that we’ll access the following method that will do the job:

The first argument we provide to the decode(_:from:) method is the type that we want to decode to. The second is the original property list data as a Data object.

The above returns a CustomShape object, which is being assigned to the current instance represented by the self keyword.

And that’s all! It’s that easy to initialize an object with the contents of a property list file.

Here’s the entire init method:

To use the above, first it’s necessary to get the URL to the DefaultShape.plist file in the app bundle, and then create a CustomShape instance using the custom initializer:

Encoding to property list

Let’s see now how we can perform the opposite and save an instance of the CustomShape type as a plist file. Suppose that we set new values to some properties which we want to store persistently:

For that purpose, let’s define the following method in the CustomShape structure:

The provided argument will be the name we want to give to the generated file. Obviously, that file is going to have the .plist extension.

To keep things simple, let’s say that we will save to documents directory of the app. The first thing in the save(as:) method is to compose the URL of the file that we’ll save to:

The first line returns the URL to the documents directory. The second appends the given file name to the URL, and the last one appends the .plist extension.

In a do-catch once again, at first we’ll encode the current CustomShape instance (self) to a property list data. We will do so using the PropertyListEncoder class and the encode(_:) method:

It’s so simple; a single line only!

The resulting encodedData object is a Data instance, and we can write it on disk like so:

Here’s the save(as:) method:

The above method can now be used as follows:

Summary

In this post I showed you how to decode data from property list files into a custom type, and encode and save back to plist as well. All that using the Codable type, PropertyListEncoder and PropertyListDecoder APIs. However, things are not always ideal, and you may want to avoid including all properties in an encoding or decoding process. Or, you may have properties of data types that cannot be encoded or decoded at all. All that are being discussed in the second post about property lists in Swift. Don’t miss it!

Stay Up To Date

Subscribe to my newsletter and get notifiied instantly when I post something new on SerialCoder.dev.

    We respect your privacy. Unsubscribe at any time.