Editing Plist Files

Editing

In this example, we will be editing a Launchd plist file.

When we wish to perform an edit operation on a plist object, we (almost always) are calling an accessor method on the Plist Object. We may call the method directly on the object, like this

  launchd_plist.watch_paths ["/path1", "/path2", ...]

However it gets a bit repetitive when there are many such plist keys to set

  launchd_plist.label "com.mydomain.myapp"
  launchd_plist.program "/path/to/myapp"
  launchd_plist.launch_only_once true
  # etc...
  launchd_plist.save

plist.edit do

So instead we can invoke an convenience edit block on our plist object, which will just instance_eval(&blk) the block.

  launchd_plist.edit do
    label "com.mydomain.myapp"
    program "/path/to/myapp"
    launch_only_once true
    # etc...
    save
  end

plist.<< do

The << operator can alternatively be used, interchangeably. Its just another way of writing plist.edit.

  launchd_plist.<< do
    label "com.mydomain.myapp"
    program "/path/to/myapp"
    launch_only_once true
  end
  launchd_plist.save

Editing operations

Certain kinds of edit operation are available on the plist keys. These are useful when we want to treat the plist keys like we would an Array object or a Hash object. Methods like Plist4r::Plist#select, Plist4r::Plist#map, Plist4r::Plist#delete_if, Plist4r::Plist#clear and similar. Such methods are all documented in Plist4r::Plist.

Plist Data (CFData / NSData)

A plist file supports key-value pairs in several types, including Base64 encoded binary data. For example, a plist key that stores binary data might look something like this

  <data>
      PEKBpYGlmYFCPA==
  </data>

When its decoded, this is a byte stream (8-bit bytes), of some finite length. In Ruby we have no dedicated class to represent this, but instead can use a ruby String as a binary string.

In Plist4r, we extend String with 2 methods, String#blob= and String#blob?. This allows us to identify and differentiate a binary string from a regular string object.

So to store binary data into a plist data key…

  @plist = Plist4r.new
  bstr = "my_binary_data"
  bstr.blob = true # mark as a binary string
  @plist.store "MyData", bstr

…and to read or inspect the binary data in Ruby from a Plist4r::Plist object

  @plist.my_data
  => "my_binary_data"
  @plist.my_data.blob?
  => true

If we forget the blob? and blob= methods, then we simple are storing and reading our data as a regular textual String. So heh, just remember to set blob= true at some point.

If we want to perform byte stream IO operations on our binary string…

  # wrap it in a StringIO object
  stream = StringIO.new(@plist.my_data)
  next_byte = stream.getc # or stream.putc(int) to write a byte

See www.ruby-doc.org/core/classes/IO.html and www.ruby-doc.org/core/classes/StringIO.html for more information about the IO object classes.

Plist Types

The class Plist4r::PlistType allows you to save those more complex data structures to specific plist keys in a plist file. For example the method Plist4r::PlistType::Launchd#socket will construct a Socket entry for a launchd plist file.

If you are developing a custom application, and intend to exchange data in a custom plist file format, it may be worth writing a custom Plist Type. In which case please see the DeveloperGuide for more info.