unit PropListUtil; { Class for working with property list (for example, app bundle's Info.plist). Author: Phil Hess. Copyright: Copyright (C) 2010 Phil Hess. All rights reserved. License: Modified LGPL. This means you can link your code to this compiled unit (statically in a standalone executable or dynamically in a library) without releasing your code. Only changes to this unit need to be made publicly available. } {$MODE Delphi} interface uses MacOSAll, CFHelpers; {Handy routines for use with Core Foundation} type {Note: Not all CF object types are supported yet by this class} TCFPropertyList = class(TObject) private propertyList : CFPropertyListRef; function GetValue(const KeyName : string) : UnivPtr; public destructor Destroy; override; function LoadFromFile(const FileName : string) : Boolean; function GetString(const KeyName : string) : string; function GetBoolean(const KeyName : string) : Boolean; end; function GetInfoPlistString(const KeyName : string) : string; implementation destructor TCFPropertyList.Destroy; begin FreeCFRef(propertyList); end; function TCFPropertyList.LoadFromFile(const FileName : string) : Boolean; {Adapted from example ObjC code given here: http://developer.apple.com/library/mac/documentation/Cocoa/ Conceptual/PropertyLists/SerializePlist/SerializePlist.html#// apple_ref/doc/uid/10000048i-CH7-SW5} var plistFileName : CFStringRef; fileURL : CFURLRef; resourceData : CFDataRef; errorCode : SInt32; errorString : CFStringRef; begin Result := False; FreeAndNilCFRef(propertyList); {In case something previously loaded} AnsiStrToCFStr(FileName, plistFileName); fileURL := CFURLCreateWithFileSystemPath( kCFAllocatorDefault, plistFileName, kCFURLPOSIXPathStyle, {Interpret as POSIX path} False); {Not a directory} {Note that if file name is not absolute, treated relative to working directory.} FreeCFRef(plistFileName); {Read the XML file. Note getting resource data, not specified properties.} try if not CFURLCreateDataAndPropertiesFromResource( kCFAllocatorDefault, fileURL, @resourceData, {Place to put XML file's data} nil, nil, errorCode) then Exit; {Description of function suggests resourceData might be non-null even if failure, so release below.} {Reconstitute the dictionary using the XML data.} propertyList := CFPropertyListCreateFromXMLData( kCFAllocatorDefault, resourceData, kCFPropertyListImmutable, @errorString); if Assigned(propertyList) then Result := True else FreeCFRef(errorString); //return this too? finally FreeCFRef(fileURL); FreeCFRef(resourceData); end; end; {TCFPropertyList.LoadFromFile} function TCFPropertyList.GetValue(const KeyName : string) : UnivPtr; {Retrieve key's CF value from property list.} var KeyRef : CFStringRef; begin Result := nil; if not Assigned(propertyList) then {Error - list not loaded?} Exit; if CFGetTypeID(propertyList) <> CFDictionaryGetTypeID then {Not valid?} Exit; AnsiStrToCFStr(KeyName, KeyRef); Result := CFDictionaryGetValue(propertyList, KeyRef); FreeCFRef(KeyRef); end; function TCFPropertyList.GetString(const KeyName : string) : string; {Retrieve key's string value from property list.} var Value : UnivPtr; begin Result := ''; Value := GetValue(KeyName); if not Assigned(Value) then {Key not found?} Exit; if CFGetTypeID(Value) = CFStringGetTypeID then {Value is a string?} Result := CFStrToAnsiStr(Value, kCFStringEncodingUTF8); //will always be UTF8 end; function TCFPropertyList.GetBoolean(const KeyName : string) : Boolean; {Retrieve key's Boolean value from property list.} var Value : UnivPtr; begin Result := False; Value := GetValue(KeyName); if not Assigned(Value) then {Key not found?} Exit; if CFGetTypeID(Value) = CFBooleanGetTypeID then {Value is a Boolean?} Result := CFBooleanGetValue(Value); end; function GetInfoPlistString(const KeyName : string) : string; {Retrieve key's string value from app bundle's Info.plist file.} var BundleRef : CFBundleRef; KeyRef : CFStringRef; ValueRef : CFTypeRef; begin Result := ''; BundleRef := CFBundleGetMainBundle; if BundleRef = nil then {Executable not in an app bundle?} Exit; AnsiStrToCFStr(KeyName, KeyRef); try ValueRef := CFBundleGetValueForInfoDictionaryKey(BundleRef, KeyRef); if CFGetTypeID(ValueRef) <> CFStringGetTypeID then {Value not a string?} Exit; Result := CFStrToAnsiStr(ValueRef, kCFStringEncodingUTF8); //will always be UTF8 finally FreeCFRef(KeyRef); end; end; {GetInfoPlistString} end.