Parsing plist using golang
golang

A couple of days back I had to parse a iOS plist a.k.a. property list file so that I can change the contents of it programmatically. The issue was I didn’t know how I could. So I took this question to Google and the first link was for a package from golang: plist

So I researched a bit for it and figured out how to use it to my requirement.

First I had to create a struct

//InfoPlist : Struct for decodeing the info plist
type InfoPlist struct {
	CFBundleDevelopmentRegion            string                          `plist:"CFBundleDevelopmentRegion"`
	CFBundleExecutable                   string                          `plist:"CFBundleExecutable"`
	CFBundleIdentifier                   string                          `plist:"CFBundleIdentifier"`
	CFBundleInfoDictionaryVersion        string                          `plist:"CFBundleInfoDictionaryVersion"`
	CFBundleName                         string                          `plist:"CFBundleName"`
	CFBundlePackageType                  string                          `plist:"CFBundlePackageType"`
	CFBundleShortVersionString           string                          `plist:"CFBundleShortVersionString"`
	CFBundleURLTypes                     []InfoPlistCFBundleURLTypes     `plist:"CFBundleURLTypes"`
	CFBundleVersion                      string                          `plist:"CFBundleVersion"`
	LSApplicationCategoryType            string                          `plist:"LSApplicationCategoryType"`
	LSRequiresIPhoneOS                   bool                            `plist:"LSRequiresIPhoneOS"`
	NSAppTransportSecurity               InfoPlistNSAppTransportSecurity `plist:"NSAppTransportSecurity"`
	NSPhotoLibraryAddUsageDescription    string                          `plist:"NSPhotoLibraryAddUsageDescription"`
	UILaunchStoryboardName               string                          `plist:"UILaunchStoryboardName"`
	UIMainStoryboardFile                 string                          `plist:"UIMainStoryboardFile"`
	UIRequiredDeviceCapabilities         []string                        `plist:"UIRequiredDeviceCapabilities"`
	UISupportedInterfaceOrientations     []string                        `plist:"UISupportedInterfaceOrientations"`
	UISupportedInterfaceOrientationsIpad []string                        `plist:"UISupportedInterfaceOrientations~ipad"`
	CFBundleDisplayName                  string                          `plist:"CFBundleDisplayName"`
}

//InfoPlistCFBundleURLTypes : Subsctruct for plist decoding
type InfoPlistCFBundleURLTypes struct {
	CFBundleTypeRole   string   `plist:"CFBundleTypeRole"`
	CFBundleURLName    string   `plist:"CFBundleURLName"`
	CFBundleURLSchemes []string `plist:"CFBundleURLSchemes"`
}

//InfoPlistNSAppTransportSecurity : Substruct for plist decoding
type InfoPlistNSAppTransportSecurity struct {
	NSAllowsArbitraryLoads bool `plist:"NSAllowsArbitraryLoads"`
}

Then I had to read the file and parse it.

	infoFile, err := ioutil.ReadFile("Info.plist")

	if err != nil {
		fmt.Println("File read failed")
		return
	}

	var data InfoPlist
	buf := bytes.NewReader(infoFile)
	decoder := plist.NewDecoder(buf)
	err = decoder.Decode(&data)

	if err != nil {
		fmt.Println(err)
	}

Now from here I can start printing the data accessing each keys from the plist file. You can even change the values.

fmt.Println(data.CFBundleDisplayName)
data.CFBundleShortVersionString = "2.2"
data.CFBundleVersion = "111"

Now once you have done that, you can encode it again and print the new plist or save it like below.

	newBuf := &bytes.Buffer{}
	encoder := plist.NewEncoder(newBuf)
	err = encoder.Encode(data)

	if err != nil {
		fmt.Println(err)
	}
	ioutil.WriteFile("tmpinfo.plist", newBuf.Bytes(), 0644)

That’s it, parsing and constructing plist files are so damn easy with this package, kudos to the package developer. If you have any doubts, do let me know.