1

I need to exclude specific keys found in an Info.plist for my app if they are found in another Hash. Currently I can access individual keys in Info.plist like this

setting4Str = `/usr/libexec/PlistBuddy -c \"print :MySettingsDict:setting4" {settings_file}`

and I can get the "setting 4 text" string set at this key in the plist dictionary...

However, I want to be able to iterate over all keys of this MysettingsDict. Does anyone have a method to convert an iOS XML plist to a ruby Dictionary?

<?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>CFBundleDisplayName</key>
    <string>My App</string>
    <key>CFBundleExecutable</key>
    <string>${EXECUTABLE_NAME}</string>
    <key>CFBundleIdentifier</key>
    <string>com.company.appname</string>
    <key>CFBundleInfoDictionaryVersion</key>
    <string>6.0</string>
    <key>CFBundleLocalizations</key>
    <array>
        <string>en</string>
        <string>de</string>
        <string>it</string>
        <string>fr</string>
        <string>ru</string>
        <string>es</string>
    </array>
    <key>CFBundleName</key>
    <string>${PRODUCT_NAME}</string>
    <key>CFBundlePackageType</key>
    <string>APPL</string>
    <key>CFBundleShortVersionString</key>
    <string>1.3.2</string>
    <key>CFBundleSignature</key>
    <string>????</string>
    <key>CFBundleURLTypes</key>
    <array>
        <dict>
            <key>CFBundleURLName</key>
            <string>com.company.mySchemeName</string>
            <key>CFBundleURLSchemes</key>
            <array>
                <string>mySchemeName</string>
            </array>
        </dict>
    </array>
    <key>CFBundleVersion</key>
    <string>1.3.15.04170</string>
    <key>Internal version</key>
    <string>1.3.15.04170</string>
    <key>LSRequiresIPhoneOS</key>
    <true/>
    <key>MysettingsDict</key>
    <dict>
        <key>setting1</key>
        <false/>
        <key>setting2</key>
        <false/>
        <key>setting3</key>
        <false/>
        <key>setting4</key>
        <string>setting 4 text</string>
        <key>setting5</key>
        <string>setting 5 text</string>
        <key>setting6</key>
        <false/>
        <key>setting7</key>
        ...
jcpennypincher
  • 3,970
  • 5
  • 31
  • 44

3 Answers3

6

Addition for those who want to parse binary Info.plist.

info_plist = File.read("Info.plist") # read binary plist
IO.popen('plutil -convert xml1 -r -o - -- -', 'r+') {|f|
  f.write(info_plist)
  f.close_write
  info_plist = f.read # xml plist
}

Now info_plist is an xml string. To quickly extract one property you can use regex:

app_bundle = info_plist.scan(/<key>CFBundleIdentifier<\/key>\s+<string>(.+)<\/string>/).flatten.first

Or parse xml and convert to hash.

Roman B.
  • 3,598
  • 1
  • 25
  • 21
3

Use the Nokogiri Gem to parse the xml then output it as a ruby hash.

Here is another stackoverflow answer that gives an example if the docs are not enough.

You could also use the Ruby Hash delete_if and has_key? methods to remove or allow whatever you want.

Community
  • 1
  • 1
blnc
  • 4,384
  • 1
  • 28
  • 42
1

If you are looking for gems, there are at least 2 options:

  1. plist_lite
  2. plist

plist is written in pure Ruby while plist_lite is written in C.

If performance is your concern, use plist_lite, it's over 5 times faster than plist. if you need some rich features like encoding/decoding non-primitive data types or formatting, use plist.

Usage of plist_lite

require 'plist_lite'
plist = PlistLite.dump({foo: 'bar', ary: [1,2,3], time: Time.at(0)})
# => "<?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>foo</key><string>bar</string><key>ary</key><array><integer>1</integer><integer>2</integer><integer>3</integer></array><key>time</key><date>1970-01-01T00:00:00Z</date></dict></plist>"
PlistLite.load(plist)
# => {"foo"=>"bar", "ary"=>[1, 2, 3], "time"=>1970-01-01 00:00:00 UTC}

Usage of plist

result = Plist.parse_xml('path/to/example.plist')

result.class
=> Hash

"#{result['FirstName']} #{result['LastName']}"
=> "John Public"

result['ZipPostal']
=> "12345"
Weihang Jian
  • 7,826
  • 4
  • 44
  • 55