Recent from talks
Nothing was collected or created yet.
Property list
View on Wikipedia
| Property List | |
|---|---|
| Filename extension | .plist |
| Internet media type | application/x-plist |
| Uniform Type Identifier (UTI) | com.apple.property-list |
| Developed by | Apple Computer and GNUstep, formerly NeXT |
| Type of format | Serialization of dictionary objects. |
In the macOS, iOS, NeXTSTEP, and GNUstep programming frameworks, property list files are files that store serialized objects. Property list files use the filename extension .plist, and thus are often referred to as p-list files.
Property list files are often used to store a user's settings. They are also used to store information about bundles and applications, a task served by the resource fork in the old Mac OS.
Property lists are also used for localization strings for development. These files use the .strings or .stringsdict extensions. The former is a "reduced" old-style plist containing only one dictionary without the braces (see propertyListFromStringsFileFormat),[1] while the latter is a fully fledged plist. Xcode also uses a .pbxproj extension for old-style plists used as project files.
Representations
[edit]Since the data represented by property lists is somewhat abstract, the underlying file format can be implemented many ways. Namely, NeXTSTEP used one format to represent a property list, and the subsequent GNUstep and macOS frameworks introduced differing formats.
NeXTSTEP
[edit]Under NeXTSTEP, property lists were designed to be human-readable and edited by hand, serialized to ASCII in a syntax somewhat like a programming language. This same format was used by OPENSTEP.
- Strings are represented in C literal style:
"This is a plist string\n"; simpler, unquoted strings are allowed as long as they consist of alphanumericals and one of _$+/:.-. - Binary data are represented as:
< [hexadecimal codes in ASCII] >. Spaces and comments between paired hex-codes are ignored. - Arrays are represented as:
( "1", "2", "3" ). Trailing commas are tolerated. - Dictionaries are represented as:
{ "key" = "value"; ... }. The left-hand side must be a string, but it can be unquoted. - Comments are allowed as:
/* This is a comment */and// This is a line comment. - As in C, whitespace are generally insignificant to syntax. Value statements terminate by a semicolon.
One limitation of the original NeXT property list format is that it could not represent an NSValue (number, Boolean, etc.) object. As a result, these values would have to be converted to string, and "fuzzily" recovered by the application.[2] Another limitation is that there is no official 8-bit encoding defined.[3]
The defaults utility, introduced in OPENSTEP (1996), can be used to manipulate plist files used for storage of preferences (known as defaults in NeXTSTEP, hence the name) on the command line via their preferences domain, and this utility can be used to edit arbitrary plist files. This utility superseded three older commands (dread, dwrite, and dremove).[4]
GNUstep
[edit]GNUstep adopts the NeXTSTEP format, with additions for representing NSValue and NSDate data types. The new typed entries have the form <*T...>, where T is a one-letter type code. For example, an NSValue of Boolean YES is represented as <*BY> and NSDate objects are represented as <*DYYYY-MM-DD HH:MM:SS +ZZZZ>.[5][6] Binary data can also use the more efficient base64 format as <[ b64... ]>. The 8-bit problem is implicitly solved as well, as most deployments use UTF-8.
GNUstep also has its own binary format, NSPropertyListGNUstepBinaryFormat, implemented in NSSerialization. This format is defined recursively like the textual formats, with a single-byte type marker preceding some data. A form of string interning is supported via a GS-extension shouldBeCompact switch.[7]
Two relative independent plist handlers are found in GNUstep: the CFPropertyList in libs-core-base (CoreFoundation), and the NSPropertyList in libs-base (Foundation Kit). Both support the binary and XML forms used by macOS to some degree, but the latter is a lot more complete. For example, the two GNUstep-specific formats are only handled in the latter.[8]
GNUstep provides a set of plist command-line tools based on NSPropertyList, including a version of pl[9] and defaults.[4]
macOS
[edit]While macOS can also read the NeXTSTEP format, Apple sets it aside in favor of two new formats of its own, one XML-based and the other binary. Apple also has a partially-compatible JSON format (NSJSONSerialization).
History
[edit]In Mac OS X 10.0, the NeXTSTEP format was deprecated, and a new XML format was introduced, with a public DTD defined by Apple. The XML format supports non-ASCII characters and storing NSValue objects (which, unlike GNUstep's ASCII property list format, Apple's ASCII property list format does not support).[10]
Since XML files, however, are not the most space-efficient means of storage, Mac OS X 10.2 introduced a new format where property list files are stored as binary files. Starting with Mac OS X 10.4, this is the default format for preference files. In Mac OS X 10.7, support for reading and writing files in JSON format was introduced. JSON and property lists are not fully compatible with each other, though. For example, property lists have native date and data types, which the JSON format does not support. Conversely, JSON permits null values while property lists do not support explicit nulls.
Tooling
[edit]The old defaults tool from NeXTSTEP remains available. The /usr/libexec/PlistBuddy command provides an interactive plist editor. It can also be scripted.[11]
The plutil utility (introduced in Mac OS X 10.2) can be used to check the syntax of property lists, or convert a property list file from one format to another. It also supports converting plists to Objective-C or Swift object literals.[12] Like the Cocoa NSPropertyListSerialization it is built on, it takes "old-style" inputs, but does not convert to this type. (The Cocoa NSSerializer from before Mac OS X 10.2 emits old-styled output.)
The pl utility is introduced in Mac OS X v10.5. It takes any input and tries to generate "old-style" plists.[13][a] Like the GNUstep version, it appears to use the description property of Foundation types found in plists, which Apple has specified to produce valid old-style plists.[b]
In terms of the internals, Apple provides an open source parser for old style, XML, and binary formats in their C Core Foundation code as CFPropertyList. However, all the utilities and most parts of the system use the closed-source NSPropertyList parser from the Obj-C Foundation Kit. The Swift reimplementation is open source, but is not guaranteed to be identical.
Format
[edit]XML and JSON property lists are hand-editable in any text editor. Additionally, Apple provides support in Xcode for editing property lists in a hierarchical viewer/editor that can handle plists formatted in binary or XML, but not JSON. As of Mac OS X 10.4, Apple provides an AppleScript interface for reading property list files through the System Events application. As of Mac OS X 10.5, Apple provides an AppleScript interface for editing, creating and writing property list files as well.[14]
For the XML format, the tags, related Foundation classes and CoreFoundation types, and data storage formats are as follows:
| Foundation class | CoreFoundation type | XML Tag | Storage format |
|---|---|---|---|
| NSString | CFString | <string> | UTF-8 encoded string |
| NSNumber | CFNumber | <real>, <integer> | Decimal string; reals can have exponents, or be Infinity (inf) or NaN (nan). |
| NSNumber | CFBoolean | <true/>, <false/> | No data (tag only) |
| NSDate | CFDate | <date> | ISO 8601 formatted string |
| NSData | CFData | <data> | Base64 encoded data |
| NSArray | CFArray | <array> | Can contain any number of child elements. Can be empty. |
| NSDictionary | CFDictionary | <dict> | Alternating <key> tags and plist element tags. Can be empty. |
The binary file format is documented in a comment block in the Core Foundation C code source file (CF/CFBinaryPList.c) for Apple's open sourced implementation of binary plists in its Foundation library.[15] Apple describes the implementation as opaque in its plist(5) manual page documentation,[16] which means that reliance on the format is discouraged. In the binary file format the magic number (the first few bytes of the file which indicate that it's a valid plist file) is the text bplist, followed by two bytes indicating the version of the format.
The binary file can store some information that cannot be captured in the XML or JSON file formats. The array, set and dictionary binary types are made up of pointers - the objref and keyref entries - that index into an object table in the file. This means that binary plists can capture the fact that - for example - a separate array and dictionary serialized into a file both have the same data element stored in them. This cannot be captured in an XML file. Converting such a binary file will result in a copy of the data element being placed into the XML file. Additionally the binary file has a UID type that is used to identify data items when serialized. The complete list of data that can be stored taken from the C code source file is as follows:
| Foundation class | CoreFoundation type | Object type | Marker byte | Encoded data |
|---|---|---|---|---|
| nil | nil | null (v"1?"+) | 0000 0000 | — |
| NSNumber | CFBoolean | bool | 0000 1000 (false) 0000 1001 (true) |
— |
| NSURL | CFURL | url (v"1?"+) | 0000 1100 (base string) 0000 1101 (string) |
string: URL string in recursive encoding (as in "string object format?"); base string: the same, but with a base URL encoded first. |
| NSUUID | CFUUID | uuid (v"1?"+) | 0000 1110 | 16 bytes of UUID |
| fill | 0000 1111 | nothing - just a padding | ||
| NSNumber | CFNumber | int | 0001 nnnn | # of bytes is 2^nnnn, big-endian bytes (1, 2, 4, or 8) |
| NSNumber | CFNumber | real | 0010 nnnn | # of bytes is 2^nnnn, big-endian bytes (4 or 8) |
| NSDate | CFDate | date | 0011 0011 | 8 byte float follows, big-endian bytes; seconds from 1/1/2001 (Core Data epoch) |
| NSData | CFData | data | 0100 nnnn [int] | nnnn is number of bytes unless 1111 then int count follows, followed by bytes |
| NSString | CFString | string | 0101 nnnn [int] | ASCII string, nnnn is # of chars, else 1111 then int count, then bytes |
| NSString | CFString | string | 0110 nnnn [int] | Unicode string, nnnn is # of chars, else 1111 then int count, then big-endian 2-byte uint16_t |
| NSString | CFString | string (v"1?"+) | 0111 nnnn [int] | UTF-8 string, nnnn is # of chars, else 1111 then int count, then bytes |
| UID | 1000 nnnn | nnnn+1 is # of big-endian bytes (1, 2, 4, or 8). Unsigned int, only produced by NSKeyedArchiver (see below).[17] | ||
| NSArray | CFArray | array | 1010 nnnn [int] | objref* nnnn is count, unless '1111', then int count follows |
| NSOrderedSet | ordset (v"1?"+) | 1011 nnnn [int] | objref* nnnn is count, unless '1111', then int count follows | |
| NSSet | CFSet | set (v"1?"+) | 1100 nnnn [int] | objref* nnnn is count, unless '1111', then int count follows |
| NSDictionary | CFDictionary | dict | 1101 nnnn [int] | keyref* objref* nnnn is count, unless '1111', then int count follo |
Note the v"1?"+ note in many types. This means that the marker byte is only found in files with a format version no lower than the "1?" magic number. The precise way to parse them is more nebulous than the way to parse legacy types, since the CFBinaryPlist implementation only handles version "0?". In practice, these types are never encountered, since NSKeyedArchiver is already capable of capturing these information.
A table of offsets follow the object table, which is then followed by a trailer containing information on the size and location of the two tables.
Serializing to plist
[edit]Since property lists do not capture all the information and data types required to describe an arbitrary object, an extra layer of encoding and decoding is often done. The OpenStep specification abstracts the operation of serializing any NSObject under the NSCoding protocol. Any class implementing this protocol can have its instances serialized by a NSCoder subclass to some other format.[18] Two main coders exist for the purpose of serializing objects to plists:
- NSArchiver, which converts an object into a block of binary data somewhat like a tagged struct. This class is part of OpenStep, although no concrete format has been defined. In practice, one can use it to serialize an object to a file (skipping the plist), or to embed the data in a plist. It must be read and written in the same order as written. The introduction of NSKeyedArchiver deprecates its use.
- NSKeyedArchiver, introduced in Mac OS X 10.2, transforms the object into an NSDictionary. The main improvement of this format for programmers is that it accesses members not by a fixed order, but by string keys. Internally, it somewhat recapitulates the binary plist format by storing an object table array called $objects in the dictionary. Everything else, including class information, is referenced by a UID pointer. A $top entry under the dict points to the top-level object the programmer was meaning to encode.[17]
Among other things, using an archiver allows for new datatypes to be encoded without changing the plist format itself and it is the preferred way for Apple to encode things like NSSets and null values. Parsing the formats do prove a bit harder, since one more layer must be followed even for some classes plists were supposed to support. Like the binary format which also has an object table, it is possible to create circular references in NSKeyedArchiver. Since there is not a UID data type in XML, the integers are stored in a dictionary under the key "CF$UID".[19]
Apple publishes an open-source NSKeyedArchiver in Swift Corelibs Foundation; like the closed-source Apple Foundation, it restricts output formats to binary and XML only. It also has some test cases showing the results of serialization.[20] GNUstep also has a compatible implementation, which does not limit output formats.[21]
Path language
[edit]There is not a single, standardized path language for property lists like XPath does for XML, but informal conventions used by various programs exist.
- A dot syntax version is found in the keypath argument of Apple's plutil. It appears to derive from
(id) -[NSObject(NSKeyValueCoding) valueForKeyPath:].[12] - A different format is used by PlistBuddy, with a colon syntax for indexing.[11]
Neither format is able to express a key with the separator character in it.
Other platforms
[edit]Windows
[edit]Although best known on Apple or Darwin systems, including iOS and macOS, plist files are also present on Windows computers when Apple software, such as iTunes[22] or Safari[23] are installed. On Windows, the files are typically binary files,[24] although some applications may generate PLIST files in the other formats.
On Windows the Apple plist files are stored in the user's home directory under %USERPROFILE%\AppData\Roaming\Apple Computer. These plist files on Windows typically store preferences and other information, rather than using the Windows registry.
Options for editing PLIST files on Windows are not as extensive as on macOS. If the file is in the XML or JSON format with care a text editor such as Notepad++ can be used. Apple ships a plutil.exe within its "Apple Application Support" package (which is part of iTunes), and it is identical to its macOS counterpart.
NetBSD
[edit]Introduced in 2006 and first released with NetBSD#4.0 (2007) is a proplib library, which can be used for serialising data between the kernel and userland. It implements part of the XML plist language.[25]
One of the sample users of proplib is the second revision of the sysmon envsys framework for system monitoring.
NetBSD's proplib library has also been ported to DragonFly in 2010, and is available since DragonFly BSD#2.8.[26]
Cross-platform
[edit]- Facebook's open-source reimplementation of the Xcode build tool, xcbuild, contains a plist library as well as plutil and PlistBuddy. These cross-platform utilities are written in C++.[27]
- Python has a builtin
plistlibmodule to read and write plist files, in Apple's XML or in binary (since Python 3.4).[28] ProperTree is a cross-platform editor that makes use of this library.[29]- A third-party library called ccl-bplist has the additional ability to handle NSKeyedArchiver UIDs.[19]
- Go has a
plistpackage that supports four types of plists: OpenStep text, GNUStep text, Apple XML, and Apple Binary. It also handles UIDs in XML and binary formats.[2] - Dart has a third party library called propertylistserialization[30] which also handles NSKeyedArchiver UIDs.
See also
[edit]Notes
[edit]References
[edit]- ^ "NSString class reference: propertyListFromStringsFileFormat". GNUstep. Retrieved February 17, 2020.
- ^ a b "Package plist". godoc.org.
When Unmarshal encounters an OpenStep property list, it will enter a relaxed parsing mode: OpenStep property lists can only store plain old data as strings, so we will attempt to recover integer, floating-point, boolean and date values wherever they are necessary.
- ^ "Old-Style ASCII Property Lists". Apple Developer (Property List Programming Guide). Retrieved February 16, 2020.
- ^ a b – Linux General Commands Manual from ManKier.com
- ^ Frith-Macdonald, Richard (2004). "NSPropertyListSerialization class documentation". GNUstep.
- ^ "Property Lists". GNUstepWiki.
- ^ "NSSerialization documentation". GNUstep. source code
- ^ "Types and Constants: NSPropertyListFormat". gnustep.org.
- ^ – Linux General Commands Manual from ManKier.com
- ^ "appleexaminer.com: "PLIST files"". Archived from the original on February 19, 2014. Retrieved February 27, 2014.
- ^ a b – Darwin and macOS System Manager's Manual
- ^ a b – Darwin and macOS General Commands Manual
- ^ – Darwin and macOS General Commands Manual
- ^ "AppleScript: Property Lists". April 24, 2009. Archived from the original on April 24, 2009.
- ^ a b "CF/CFBinaryPList.c". Apple Open Source. Retrieved January 6, 2020.
- ^ – Darwin and macOS File Formats Manual
- ^ a b Caithness, Alex (April 4, 2012). "Geek post: NSKeyedArchiver files – what are they, and how can I use them?". Digital Investigation.
The CF$UID data type in Listing 2 is a dictionary with a single key ("CF$UID") which is followed by an integer number (this layout is what you will see when the property list is represented in XML; in the raw binary format the "UID" data type is a separate entity which doesn't require the dictionary structure). These data types represent a reference to another entity in the "$objects" array. The number of the CF$UID gives the position of the array.
- ^ "NSCoder.html". OpenStep specification.
- ^ a b CCLForensics Group (January 15, 2020). "cclgroupltd/ccl-bplist". GitHub.
- ^ "apple/swift-corelibs-foundation: NSKeyedArchiver.swift". GitHub. December 14, 2021. tests
- ^ "NSKeyedArchiver documentation". GNUstep documentation. Retrieved February 23, 2020. source code
- ^ Hummert, Christian; Humphries, Georgina Louise (2022), Hummert, Christian; Pawlaszczyk, Dirk (eds.), "Property Lists", Mobile Forensics – The File Format Handbook: Common File Formats and File Systems Used in Mobile Devices, Cham: Springer International Publishing, pp. 157–165, doi:10.1007/978-3-030-98467-0_6, ISBN 978-3-030-98467-0
- ^ "How does the Apple Safari web-browser store its settings on Windows?". Super User.
- ^ "GitHub - microsoft/NSPlist: NSPlist". May 3, 2019 – via GitHub.
- ^ Jason R. Thorpe. "proplib — property container object library". NetBSD.
- ^ Jason R. Thorpe. "proplib — property container object library". DragonFly BSD.
- "proplib -- property container object library". DragonFly On-Line Manual Pages.
- ^ "facebook/xcbuild/Libraries/plist". GitHub. Retrieved February 17, 2020.
- ^ "plistlib — Generate and parse Mac OS X .plist files". Python 3 documentation. Retrieved January 6, 2020.
- ^ "corpnewt/ProperTree: Cross platform GUI plist editor written in python". GitHub. February 17, 2020. Retrieved February 17, 2020.
- ^ "propertylistserialization | Dart package". Dart packages. Retrieved September 14, 2024.
External links
[edit]- The plist(5) manual page at developer.apple.com
- Excel add-in for creating plist file from Excel table
- Java API for reading/writing various plist formats] – compatible with Android
- EBNF grammar of GNUstep plists
Property list
View on Grokipedia.plist file extension.[1] In Apple development, the most prominent example is the Info.plist file, which provides essential bundle information like version numbers, permissions, and supported orientations for apps, frameworks, and plug-ins.[3] This format's design emphasizes simplicity and portability, allowing tools like Xcode to generate and edit them automatically while supporting localization and platform-specific customizations.[3] Beyond Apple platforms, property lists have influenced similar structured data formats in other systems, underscoring their role in modern software configuration.[4]
Overview
Definition and Purpose
A property list, often abbreviated as plist, is a standardized data serialization format in Apple's operating systems that represents a hierarchy of simple objects, enabling easy storage, transmission, and reconstruction of structured data. It serves as a lightweight mechanism for persisting small amounts of data—typically under a few hundred kilobytes—in a platform-independent manner, integrated natively with Core Foundation and Foundation frameworks.[5][6] At its core, a property list comprises basic object types including strings (NSString/CFString), numbers (NSNumber/CFNumber, encompassing integers, floats, and booleans), dates (NSDate/CFDate), binary data blobs (NSData/CFData), arrays (NSArray/CFArray of property list objects), and dictionaries (NSDictionary/CFDictionary mapping keys to property list values). These elements can be nested arbitrarily to create complex structures, such as a dictionary containing an array of date-number pairs, ensuring the entire hierarchy remains serializable without loss of type information.[6][5] In the Apple ecosystem, property lists are predominantly used for application preferences, where user settings are saved in files like com.example.app.plist; bundle metadata in Info.plist files, which detail executable configurations such as version numbers and permissions; launch configurations for daemons and agents via launchd.plist; and serializing transient objects for inter-process communication or caching. This format's simplicity and efficiency make it ideal for these roles, supporting quick reads and writes without requiring a full database.[3][5] Compared to JSON or YAML, property lists provide enhanced type safety through native support for dates and binary data—avoiding the need to encode them as strings, which can lead to parsing ambiguities—while offering tighter integration with Apple's APIs via classes like NSPropertyListSerialization, unlike the more generic handling required for JSON. Property lists can be serialized in human-readable XML or efficient binary formats.[7][8] For illustration, a basic XML property list with a simple key-value pair appears as:<?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>UserName</key>
<string>Alice</string>
</dict>
</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>UserName</key>
<string>Alice</string>
</dict>
</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>Scores</key>
<array>
<real>95.5</real>
<integer>88</integer>
</array>
</dict>
</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>Scores</key>
<array>
<real>95.5</real>
<integer>88</integer>
</array>
</dict>
</plist>
<dict>, <key>, <string>, <array>, <real>, and <integer> to denote structure and types.[9]
Historical Development
Property lists were first introduced as a foundational data structure in NeXTSTEP, the object-oriented operating system developed by NeXT and released in 1988, where they served as a mechanism for serializing and persisting hierarchical objects such as arrays and dictionaries.[10] Designed initially for human-readable ASCII serialization, they enabled developers to store application settings and bundle information in a lightweight, portable format compatible with the system's Foundation framework.[10] In 1994, property lists evolved through the OPENSTEP specification, a collaborative standard between NeXT and Sun Microsystems that standardized the Application Kit and Foundation Kit APIs for cross-platform portability.[11] This transition emphasized architecture-independent serialization using classes like NSSerializer and NSDeserializer, restricting supported types to NSData, NSString, NSArray, and NSDictionary to ensure consistency in archiving and data transfer across environments.[11] The OPENSTEP format, an ASCII-based property list inherited by later systems, became a core element for user defaults management via NSUserDefaults and inter-application data exchange.[12] Following Apple's acquisition of NeXT in 1997, property lists were integrated into Mac OS X (later renamed macOS) with its debut in 2001, building directly on the NeXTSTEP and OPENSTEP heritage to form the basis of Cocoa's data persistence layer.[10] A significant efficiency enhancement came in 2002 with Mac OS X 10.2 (Jaguar), which introduced the binary property list format alongside the existing XML and ASCII options, allowing for more compact storage and faster read/write operations while maintaining lossless conversion between formats. Property lists expanded into mobile development with the launch of iOS in 2007, where they became essential for app configuration through files like Info.plist, which describe bundle metadata, permissions, and supported features to the system.[3] This adoption ensured consistency between macOS and iOS ecosystems, leveraging the same serialization APIs for preferences and runtime data.[3] Key milestones in the 2010s included the deprecation of the legacy ASCII OPENSTEP format for writing—retained only for reading older files—and deeper integration with Swift, introduced in 2014, via the PropertyListSerialization class in the Foundation framework.[12] This allowed Swift developers to encode and decode property lists natively, supporting modern type-safe handling of arrays, dictionaries, and primitive values without relying on Objective-C bridges.[6]Data Formats
XML Representation
The XML representation of a property list is a human-readable, structured format based on the Extensible Markup Language (XML), designed for storing serialized collections of key-value pairs and arrays in Apple ecosystems. It adheres to a specific Document Type Definition (DTD) that defines the allowable elements and their hierarchies, ensuring consistency and parseability. The root element is always<plist>, which encapsulates either a <dict> (dictionary) or <array> as the top-level object, with all content encoded in UTF-8 for Unicode support.[13][3]
The DTD schema specifies the following core elements: <dict> for dictionaries, which contain zero or more pairs consisting of a <key> element (containing parsed character data for the key string) followed immediately by a value object; <array> for ordered lists, containing zero or more value objects; <string> for text strings (parsed character data); <integer> for signed base-10 integers (parsed character data); <real> for floating-point numbers (parsed character data); <date> for dates in ISO 8601 format (YYYY-MM-DDTHH:MM:SSZ, parsed character data); <data> for binary data encoded in Base64 (parsed character data); and <true/> or <false/> for boolean values (empty elements). All tags are self-closing where empty and contain no attributes except the version attribute on <plist> (fixed to "1.0"). Dictionaries preserve the order of keys as they appear in the XML, facilitating sequential access.[13][9]
Property lists in XML format can be validated against Apple's official DTD using standard XML parsers or tools, with the DOCTYPE declaration <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> ensuring compliance. This validation checks for proper nesting, element sequencing (e.g., keys must precede values in dictionaries), and data type constraints, preventing malformed structures that could lead to parsing errors in applications.[13]
For instance, a nested dictionary representing application preferences might appear as follows:
<?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>AppName</key>
<string>MyApp</string>
<key>Preferences</key>
<dict>
<key>Theme</key>
<string>Dark</string>
<key>Volume</key>
<integer>80</integer>
<key>NotificationsEnabled</key>
<true/>
</dict>
</dict>
</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>AppName</key>
<string>MyApp</string>
<key>Preferences</key>
<dict>
<key>Theme</key>
<string>Dark</string>
<key>Volume</key>
<integer>80</integer>
<key>NotificationsEnabled</key>
<true/>
</dict>
</dict>
</plist>
Binary Representation
The binary property list (bplist) format is a compact serialization method for structured data, optimized for machine processing in Apple's operating systems. It replaces the human-readable XML representation with a dense byte stream, enabling smaller file sizes and quicker load times, making it the preferred choice for production system files such as Info.plist in macOS and iOS applications. This format adheres to version 1.0, as defined in Apple's Core Foundation implementation.[10][15] The file structure begins with an 8-byte header: the magic bytesbplist (0x62706c697374) followed by the version 00 (indicating format version 1.0). This is succeeded by the serialized objects section, where data is stored contiguously. An offset table follows, consisting of variable-sized (typically 1-8 byte) entries pointing to the start of each object. The file concludes with a fixed 32-byte trailer containing metadata, including five unused bytes, a sort version (usually 0), offset integer size (e.g., 1 byte), object reference size (e.g., 1 byte), the total number of objects (as a 64-bit integer), the UID of the top-level object, and the offset to the table itself. These elements allow efficient random access to objects during parsing.[15][16]
Objects are referenced via unique identifiers (UIDs), which are indices into the offset table, promoting deduplication and compactness for repeated values. Each object begins with a type specifier byte, where the high 4 bits denote the data type and the low 4 bits handle inline sizes for small payloads. Simple types include: 0x00 for null; 0x08 (false) and 0x09 (true) for booleans; 0x10 to 0x1F for integers (signed, big-endian, payload size 2^{low 4 bits} bytes if low 4 bits < 15, or length-prefixed if 0x1F); 0x80 to 0x8F for UIDs (size (low 4 bits + 1) bytes). For larger or variable-sized data, the low 4 bits are set to 0xF, followed by a separate length integer (1-8 bytes) preceding the payload. Strings use 0x50-0x5F for ASCII (0-15 bytes inline) or 0x60-0x6F for UTF-16; real numbers (doubles) are 0x20-0x23 (4- or 8-byte IEEE 754); dates are 0x33 (8-byte double offset from 2001-01-01T00:00:00Z). Collections like arrays (0xA0-0xAF), sets (0xC0-0xCF), and dictionaries (0xD0-0xDF) specify count inline or via length integer, followed by reference UIDs to constituent objects (for arrays/sets) or key-value pairs (for dictionaries, with keys first). Data blobs (0x40-0x4F) follow similar length-prefixed patterns. This packing ensures small primitives are embedded directly, while larger or shared elements are referenced to minimize redundancy.[15][17]
The format's design yields significant efficiency gains: binary plists are much more compact than XML equivalents—often up to 50% smaller for complex structures—reducing storage needs and network transfer overhead, while parsing is faster due to the lack of text decoding and direct byte access. These benefits are evident in system usage, where binary plists power critical components like application bundles and preferences without the overhead of XML verbosity. Unlike XML plists, the binary variant avoids vulnerabilities inherent to XML processing, such as XML External Entity (XXE) attacks, through its rigid, non-parsable structure and strict byte-level validation.[10][18]
For illustration, consider a simple dictionary {"Disabled": true} from a launch daemon configuration (e.g., similar to telnet.plist). A valid binary structure (simplified; actual varies with padding and table entries) based on the format specification is as follows:
- Header:
62 70 6c 69 73 74 00 00(bplist00) - Objects section (offsets via table at end):
- Dictionary object (UID 0, e.g., at offset 8):
D1 01 09(dict with 1 pair; key ref 0x01 to UID 1 string; value 0x09 true, assuming true as simple object UID 0 shared or adjusted) - String object (UID 1, e.g., at offset 11):
58 44 69 73 61 62 6c 65 64(ASCII string, 8 chars: "Disabled")
- Dictionary object (UID 0, e.g., at offset 8):
- Offset table (e.g., 1-byte offsets):
08 0B(offsets to dict at 8 and string at 11) - Trailer (32 bytes, simplified):
00 00 00 00 00 00 01 01 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 13(metadata for 2 objects, top UID 0, table offset 19 decimal; exact bytes depend on positions)
<?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>Disabled</key><true/></dict></plist>).[19]
System Integrations
NeXTSTEP Implementation
In NeXTSTEP, the operating system developed by NeXT Computer from 1988 to 1997, property lists served as a core mechanism for archiving and persisting objects through the NSArchiver class, which serialized compatible objects into human-readable .plist files. This approach enabled developers to store structured data such as user preferences and application states in a lightweight, editable format, integral to the Foundation framework's object management. Property lists provided native support in NeXTSTEP for key development tools, forming the basis for Interface Builder archives that saved user interface designs as .nib files—essentially archived collections of objects like windows, menus, and controls. They also handled application resources, allowing seamless integration of configuration data within bundles. Central to this were property list-compatible classes such as NSDictionary for key-value storage and NSArray for ordered collections, which supported direct serialization via the writeToFile:atomically: method to output ASCII-formatted .plist files. A distinctive feature of property lists in early NeXTSTEP implementations was their use on the original black-and-white NeXT hardware, such as the NeXT Computer (1988) and NeXTstation (1990), for system configuration files in a simple, text-based structure. NSArchiver facilitated this by encoding objects into portable archives, ensuring compatibility across the platform's object-oriented environment. The NeXTSTEP property list design laid the groundwork for the OPENSTEP standard released in 1994, providing a standardized API that influenced later evolutions in systems like macOS.GNUstep Implementation
GNUstep, initiated in 1994 as an open-source implementation of the OpenStep specification, provides a faithful recreation of NeXTSTEP's APIs, including comprehensive support for property lists within its Foundation framework (also known as the Base library). This support enables developers to serialize and deserialize objects such as strings, numbers, dates, data, arrays, and dictionaries into human-readable formats (ASCII and XML), mirroring the original NeXTSTEP behavior while extending it for cross-platform use on Unix-like systems.[20][21] The core functionality is handled by the NSPropertyListSerialization class, which facilitates reading and writing property lists in multiple formats, including XML (conforming to Apple's plist DTD) and the legacy OpenStep ASCII format for backward compatibility. GNUstep ensures legal XML output by using standard XML escaping (e.g., entity references), prioritizing standards compliance over exact NeXTSTEP replication in edge cases. This class supports validation to confirm that data conforms to property list structure before processing, preventing errors in application configuration or data storage.[22][23] Distinct from proprietary NeXT implementations, GNUstep leverages open-source tooling such as GNUstep-make, the project's build system, to automate the generation of property list files like Info-gnustep.plist for application bundles, which define metadata such as executable names, resource paths, and document types. These tools integrate seamlessly with GNUstep applications deployed on platforms including Linux and FreeBSD, allowing property lists to manage preferences, bundle information, and inter-process communication without platform-specific adjustments.[24][21] Community-driven enhancements have expanded property list capabilities to include additional object types, such as NSURL for handling uniform resource identifiers as serializable strings or data. Furthermore, integration with tools like GORM (GNUstep Object Relationship Modeler) utilizes property lists to archive user interface designs and object relationships, enabling developers to map graphical elements to application logic stored in plist-based files.[25][26]macOS and iOS Implementation
In macOS and iOS, property lists serve as a core component of application bundles, with the Info.plist file being mandatory for all runnable code bundles to specify essential metadata such as the bundle identifier, version number, supported orientations, and document types. This file is stored at the root directory of iOS app bundles and within the Contents directory of macOS app bundles, enabling the operating system to interpret and launch applications correctly. Entitlements, implemented as separate property list files (typically named entitlements.plist), grant specific capabilities to apps, such as access to the camera, location services, or iCloud, and are embedded during the code signing process to enforce runtime permissions. User defaults, managed through the NSUserDefaults class, store application preferences and settings in property list format, allowing persistent, key-value data storage within the app's sandboxed Library/Preferences directory on both platforms. On iOS, property lists extend to sandboxed app environments, where entitlements property lists define the boundaries of file system access, network permissions, and inter-process communication to maintain app isolation. System-level configurations, including those for SpringBoard—the iOS home screen manager—rely on property lists to store user customizations like icon arrangements and wallpaper settings, typically in binary format for efficiency. Privacy manifests, introduced as property lists in iOS 17, require developers to document data collection practices for apps and embedded third-party SDKs, including reasons for accessing sensitive APIs and whether data is used for tracking, enhancing transparency in the App Store review process. Security features in macOS and iOS integrate property lists with code signing mechanisms, where provisioning profiles—a type of signed property list wrapped in Cryptographic Message Syntax (CMS)—validate app authenticity and embedded entitlements during installation and launch. The hardened runtime, enabled by default in signed apps since macOS 10.12 and iOS 11, employs code signing hashes to protect bundle resources and metadata, including property lists, from unauthorized tampering by verifying integrity against runtime modifications. These protections extend to dynamic library validation, ensuring that linked components adhere to the signed property list declarations. Evolving from their roots in NeXTSTEP, property lists in modern Apple operating systems continue to favor binary and XML formats via the PropertyListSerialization class for serialization and deserialization, though JSON alternatives exist through JSONSerialization for simpler data exchange; binary property lists remain preferred for their compactness and legacy compatibility in system and app configurations. Mac Catalyst, Apple's framework for porting iOS apps to macOS, leverages unified property list structures to bridge the platforms, allowing shared Info.plist and entitlements files to handle cross-platform bundle identification and permissions seamlessly.Tools and Manipulation
Command-Line Utilities
The primary command-line utility for managing property lists on macOS and iOS isplutil, which supports operations such as syntax validation, format conversion between XML, binary, and JSON, expansion of macros, and merging of multiple files.[27] For instance, the -lint option checks a plist for validity and reports errors like "Unexpected character < at line 1" for malformed XML, while -convert xml1 or -convert binary1 transforms files between formats, preserving data integrity.[27] Additional subcommands include -expand to resolve variable substitutions and -merge to combine plists, with detailed error reporting aiding debugging of structural issues such as invalid keys or type mismatches.[27]
Another essential tool is the defaults command, designed specifically for reading, writing, and deleting keys in user defaults databases, which are stored as property lists in ~/Library/Preferences.[28] Common syntax includes defaults write com.example.app key value to set a value (e.g., defaults write com.apple.finder AppleShowAllFiles true to toggle hidden files) and defaults read domain to output the entire plist or defaults read domain key for a specific entry.[29] This utility automatically handles serialization to binary format upon writing, making it suitable for configuring application preferences without direct file manipulation.[29]
PlistBuddy is an Apple-provided command-line tool for directly editing property list files, supporting operations like adding, setting, deleting, and querying keys and values using a simple syntax.[30] For example, /usr/libexec/PlistBuddy -c "Add :key string value" file.plist adds a new string entry, while -c "Print :key" retrieves a value, making it useful for scripted modifications without full conversion. It operates on XML or binary plists and provides error messages for invalid paths or types.[30]
Open-source alternatives extend plist handling to non-Apple platforms. In GNUstep environments, a port of plutil provides similar functionality for editing, verifying, and converting property lists, integrated into the GNUstep Base library for cross-platform Objective-C development.[31]
Best practices for these utilities emphasize their use in shell scripts for automated batch processing, such as validating multiple app configuration plists before deployment: for file in *.plist; do plutil -lint "$file" || echo "Error in $file"; done.[32] For defaults, scripts can standardize user settings across systems, like defaults write -g NSWindowShouldDragOnGesture -bool true piped into deployment tools, ensuring consistency without overwriting unrelated keys.[33] Validation errors from plutil should be logged for auditing, as in plutil -lint config.plist > validation.log 2>&1, to catch issues like duplicate keys early in CI/CD pipelines.[32]
As of macOS 12 Monterey (2021), plutil gained enhanced JSON support, allowing direct conversion with -convert json and extraction of values via -p or -extract, which simplifies integration with modern web APIs and scripting languages.[34] These command-line tools serve as lightweight alternatives to programming interfaces for shell-based automation.[28]
Programming Interfaces
In Objective-C and the Cocoa frameworks, the NSPropertyListSerialization class in Foundation provides core methods for serializing and deserializing property lists. The+dataWithPropertyList:format:options:error: method converts a property list object—such as an NSArray, NSDictionary, NSString, NSNumber, NSData, or NSDate—into an NSData instance in either XML (NSPropertyListXMLFormat_v1_0) or binary (NSPropertyListBinaryFormat_v1_0) format, with options for controlling mutability (e.g., immutable or mutable containers) and an NSError pointer for detailed error reporting on invalid objects or format issues.[6] Conversely, the +propertyListWithData:options:format:error: method parses NSData to reconstruct the original property list, validating the format and returning nil with an error if the data is malformed or contains unsupported types.[6]
In Swift, Foundation introduces PropertyListEncoder and PropertyListDecoder classes starting from iOS 12.0, macOS 10.14, tvOS 12.0, and watchOS 5.0, enabling integration with the Codable protocol for encoding and decoding custom types as property lists. PropertyListEncoder serializes Encodable objects to Data in XML or binary formats via the .xml or .binary outputFormatting options, while PropertyListDecoder deserializes Data into Decodable types, throwing errors for type mismatches or invalid structures.[35] This approach supports complex hierarchies by automatically handling conformance for standard types and allowing custom implementations for user-defined classes.[36]
GNUstep's NSPropertyListSerialization class offers equivalent functionality to its Cocoa counterpart, supporting serialization and deserialization in XML and binary formats through similar class methods. It extends compatibility by permitting non-standard types like NSValue and NSURL in property lists, which are converted to NSData or NSString representations during serialization, enabling broader use in open-source environments.[22]
For C-based applications, Core Foundation exposes property list handling via functions like CFPropertyListCreateWithData, which constructs a CFPropertyListRef from CFData in specified formats (kCFPropertyListXMLFormat_v1_0 or kCFPropertyListBinaryFormat_v1_0), returning NULL and populating a CFError on parsing failures such as invalid UTF-8 or unknown object types.[37] Companion functions like CFPropertyListCreateXMLData and CFPropertyListCreateData handle writing, with options for mutability to optimize memory usage in performance-critical scenarios.[38]
When processing large property lists, memory management is crucial, as deserialization can create deep object graphs that consume substantial heap space; developers should use autorelease pools in Objective-C or monitor allocations in Swift to avoid excessive footprint. The serialization methods in NSPropertyListSerialization and Core Foundation are inherently thread-safe due to their stateless class-based design, but applications must synchronize access to shared mutable property list instances in multi-threaded contexts to prevent data races.
Advanced Features
Serialization Process
The serialization process for property lists involves converting an object graph composed of supported Foundation types into a serialized byte stream, typically in XML or binary format, while ensuring the graph adheres to strict validity rules. Before serialization, the input object must be validated to confirm it forms a valid property list graph: this includes verifying that the graph is acyclic (no circular references), contains only supported types such asNSString, NSNumber, NSData, NSDate, NSArray, and NSDictionary, and that all dictionary keys are strings.[39][6] If validation fails, serialization methods return nil and provide an error description, such as for unsupported custom objects, which cannot be directly serialized and instead require archiving via NSKeyedArchiver to preserve class identity and additional state.[40]
Once validated, the developer selects a format—XML for human-readable output or binary for compactness—and invokes the appropriate method on NSPropertyListSerialization or its Core Foundation equivalent to write the data to a stream or file. The process recursively traverses the object graph, encoding each element according to the chosen format's structure; for instance, binary serialization uses a compact, length-prefixed representation to minimize size. To mitigate risks like stack overflows from deeply nested structures, implementations impose practical recursion depth limits, though no fixed universal limit is enforced—excessive depth may trigger runtime errors depending on the system's stack size.[6][40][41]
Deserialization reverses this by parsing the byte stream back into native objects, with options for mutability (immutable for safety or mutable for editing). Parsing includes basic error recovery, such as halting on malformed data and returning partial results if possible, but plists maintain strict typing without automatic coercion between types like strings and numbers—mismatched types result in nil or exceptions. Developers must handle errors explicitly, checking return values and error pointers to ensure integrity.[6][40]
For optimization, binary format is recommended for large datasets due to its smaller footprint and faster read/write speeds compared to XML; additionally, large NSData blobs within plists can be compressed externally using methods like NSData compression before serialization. A typical workflow in Objective-C for serializing a dictionary might proceed as follows:
NSDictionary *dict = @{@"key": @"value"};
NSError *error = nil;
NSData *serializedData = [NSPropertyListSerialization dataWithPropertyList:dict
format:NSPropertyListBinaryFormat_v1_0
options:0
error:&error];
if (serializedData && !error) {
// Write to file or stream
[serializedData writeToFile:@"example.plist" atomically:YES];
} else {
NSLog(@"Serialization failed: %@", error.localizedDescription);
}
NSDictionary *dict = @{@"key": @"value"};
NSError *error = nil;
NSData *serializedData = [NSPropertyListSerialization dataWithPropertyList:dict
format:NSPropertyListBinaryFormat_v1_0
options:0
error:&error];
if (serializedData && !error) {
// Write to file or stream
[serializedData writeToFile:@"example.plist" atomically:YES];
} else {
NSLog(@"Serialization failed: %@", error.localizedDescription);
}
propertyListWithData:options:format:error: to reconstruct the dictionary, specifying the expected format if known.[40]
Path Expression Language
The path expression language for property lists leverages Key-Value Coding (KVC) key paths, enabling navigation through nested structures in dictionaries and arrays using string-based expressions. This syntax allows developers to access, query, and perform operations on property list data via APIs such asvalueForKeyPath: on NSDictionary and NSArray objects, which are the foundational types for property lists in Apple's frameworks. Basic navigation employs dot notation to traverse dictionary keys, such as root.key.subkey, where each segment specifies a successive dictionary key. For arrays, access to specific elements uses indexed notation like array[0] in supported contexts, though in command-line tools like plutil, this may appear as array.0 to denote the zero-based index of the element.[42]
Advanced expressions incorporate collection operators prefixed with @ to aggregate or transform data across array elements, providing functionality beyond simple traversal. For instance, @count returns the number of items in a collection (e.g., children.@count yields the count of child objects), while @sum computes the total of numeric values for a specified property (e.g., [email protected] sums all amount values in the transactions array). Other operators include @avg for averages, @min and @max for extrema, @distinctUnionOfObjects for unique values, and @unionOfObjects for all values including duplicates; these require a right key path following the operator to specify the target property. Wildcards (*) can be used in some API contexts to match multiple keys or elements, and ranges (e.g., [0:5]) may denote subsets of arrays, though support varies by implementation. These operators apply directly to property list structures, as NSDictionary and NSArray conform to KVC protocols.[43]
In usage contexts, path expressions are integral to command-line tools and scripting. The defaults command supports basic key paths for reading user defaults (e.g., defaults read com.example.app root.key.subkey), which query property lists stored in the defaults system. For more complex extraction from arbitrary plist files, plutil -extract path raw file.plist uses KVC-style paths to output values, such as plutil -extract preferences.[window](/page/Window).[size](/page/Size) file.plist to retrieve a nested size value. In programming interfaces like Foundation's NSUserDefaults or Core Foundation's CFPreferences, paths enable scripted queries, for example, retrieving aggregated app preferences: let sum = dictionary.value(forKeyPath: "[email protected]") as? Double. These tools facilitate automation in shell scripts or apps without loading entire plists into memory.[29][27]
Limitations of this language include its read-only nature in many contexts, as KVC paths via valueForKeyPath: do not support creation or modification—use setValue:forKeyPath: separately for writes. It lacks full support for complex filters like [?value>10] seen in XPath or JSONPath, relying instead on separate NSPredicate objects for conditional queries (e.g., filtering arrays before applying paths). Invalid paths typically return nil or raise an NSUndefinedKeyException, requiring error handling via try-catch blocks in code or exit codes in tools (e.g., plutil returns non-zero on failure). Compared to XPath or JSONPath, KVC paths prioritize Objective-C/Swift integration over standalone querying, omitting advanced recursion or regex but excelling in collection aggregations for plist data.[42]
For example, to query nested app preferences in a plist representing user settings, a path like appPreferences.screens.@[count](/page/Count) could count available screen configurations, while [email protected] extracts unique file names from a recent-files array. If the path is invalid, such as referencing a non-existent subkey, APIs log an exception, and tools like plutil output an error message like "Unexpected character 0 at line 1," prompting verification of the plist structure.[43]
