'defaults write' to PLIST Child Value

Discussion in 'Mac Programming' started by Caleb531, Jan 2, 2010.

  1. Caleb531 macrumors 6502


    Oct 17, 2009
    I have not found an answer yet so I'm hoping someone here can help me. I don't know a great deal about PLIST files, so forgive me if I do not use the proper terminology.

    I am trying to write an AppleScript to change the value of the Info.plist file for iWork '09 Pages. I know how I can write to a value, but not how to write to a child value. I am trying to change the value CFBundleTypeRole of item 8 of CFBundleDocumentTypes. This is the script I have so far:

    I do not know how to get any further than that. Any help would be greatly appreciated.
  2. chown33 macrumors 604

    Aug 9, 2009
    Sailing beyond the sunset
    Give up. Forget it. Abandon all hope. It's an enormous PITA to use 'defaults' with any nested objects.

    Use plistbuddy instead. Google it.
  3. larkost macrumors 6502a

    Oct 13, 2007
    Ya, defaults does not work very well once you are past the first layer, and PlistBuddy is a bit better. If you are looking to change it to a single value than that might be your best option.

    Once it gets just a little more complicated I usually switch up to using Python. If you can get away without supporting older versions (so 10.5 and up), then it gets very nice to use. Here is some simple sample code:
    #!/bin/env python
    import Foundation
    # read in the data
    plistNSData = Foundation.NSData.dataWithContentsOfFile_("/Applications/iWork '09/Pages.app/Contents/Info.plist")
    plistData, format, error = Foundation.NSPropertyListSerialization.propertyListFromData_mutabilityOption_format_errorDescription_(plistNSData, Foundation.NSPropertyListMutableContainersAndLeaves, None, None)
    # search for exactly the data we want, and change it
    for thisBundleType in plistData["CFBundleDocumentTypes"]:
    	if "CFBundleTypeName" in thisBundleType and thisBundleType["CFBundleTypeName"] == "SLDocumentTypeRichTextBundle":
    		thisBundleType["CFBundleTypeRole"] = "Reader"
    # write the data back out (to a temp file)
    plistNSData, error = Foundation.NSPropertyListSerialization.dataFromPropertyList_format_errorDescription_(plistData, format, None)
    plistNSData.writeToFile_atomically_("/tmp/sampleChangedData.plist", True)
    Two notes: watch out for this web page swallowing some code or formatting (very important with Python), and you will have to change the particulars to doing what you want. Right now it is saving things back to a temp file rather than the real one.

    Oh... and I omitted all error handling code, which production code should have. So if something goes wrong this is likely to dig itself a bit of a ditch.
  4. nomar383 macrumors 65816


    Jan 29, 2008
    Rexburg, ID
    Is plistbuddy installed by default? How about all the way back to 10.4? Can someone post a simple example of this in action?

    I am trying to do a similar thing as the OP.
  5. Detrius macrumors 68000

    Sep 10, 2008
    Asheville, NC
    It's been a while since I did this, and I no longer have access to the code, but python has a plist editing library.
  6. larkost macrumors 6502a

    Oct 13, 2007
    plistlib has a lot of problems: It can't work with binary plists, it can work with plists that have dates in them, there are some oddities converting numbers, and a few other issues.

    It is far better to use things through the Obj-C bridge. As you can see from my example above, with just a few lines (maybe 2 more than using plistlib), you have Python objects that you can use and work with natively (dates are a little fiddly, but fairly easy to work with).

Share This Page