Reading a key from a preference file

I need to read the value of a key in a preference file for one of my AppleScripts. Specifically, I want to get IPXDefaultLibraryURLBookmark from the preferences for Photos.

My first attempt was to use do shell script with the defaults command. However for a binary key it seems that defaults only returns a severely truncated version of the value:

do shell script "defaults read IPXDefaultLibraryURLBookmark"
--> "{length = 900, bytes = 0x626f6f6b 84030000 00000410 30000000 ... a8010000 00000000 }"

Is there a way I can interact with the MacOS preferences system in AppleScript to get the full value of a binary key? Thanks!

IPXDefaultLibraryURLBookmark is not a binary key.

I think it’s a large base-64 string (MacOS bookmark) which you’re right isn’t technically binary. But it’s not a ‘normal’ string so either because of that or its size, the defaults command doesn’t return it in full. Is there some other way to get it?

In case it’s useful to anyone in the future, I’ve figured out a solution. It turns out that (although undocumented in the man page) there’s an export option to the defaults command that will provide the full value of all keys for a domain. If I save the export to a temporary file, it’s possible to use AppleScript’s Property List File objects to easily get the key I want.

property pPrefsDomain : ""
property pPrefItem : "IPXDefaultLibraryURLBookmark"

set vTempFolder to POSIX path of (path to temporary items folder)
set vFile to vTempFolder & pPrefsDomain & ".plist"
do shell script "defaults export " & pPrefsDomain & " " & quoted form of vFile
tell application "System Events"
	set vFile to property list file vFile
	set vPrefItemValue to text of property list item pPrefItem of vFile
end tell

set vSavedDelims to AppleScript's text item delimiters
set AppleScript's text item delimiters to {"<data>" & linefeed, linefeed & "</data>"}
set vPhotosLibraryBookmark to text item 2 of vPrefItemValue
set AppleScript's text item delimiters to vSavedDelims

This works, though the last bit is a work-around for an issue I can’t figure out, where AppleScript is returning binary data instead of base64 text. For some reason, if I do:

set vPrefItemValue to value of property list item "IPXDefaultLibraryURLBookmark" of vFile

then vPrefItemValue contains binary data, even though IPXDefaultLibraryURLBookmark is base64 text (in the plist file). I don’t know why or how it’s being converted to binary data. Or how I can convert it back. My preference would be to use value of property list item and convert back from binary to base64 text.

Does anyone know how I can convert a returned value of the form:

«data ****626F6F6B8403000000000410300000000000000000000000000000000000000000000000000000[…]000000»

back into base64 text?

You used to be able to read in defaults for all apps via ASObjC and NSUserDefaults, but it looks like they’ve restricted its access. However, a mixture of your export approach and some ASObjC code will do what I think you want:

use AppleScript version "2.4" -- Yosemite (10.10) or later
use framework "Foundation"
use scripting additions

property pPrefsDomain : ""
property pPrefItem : "IPXDefaultLibraryURLBookmark"

set vTempFolder to POSIX path of (path to temporary items folder)
set vFile to vTempFolder & pPrefsDomain & ".plist"
do shell script "defaults export " & pPrefsDomain & " " & quoted form of vFile

set theData to current application's NSData's dataWithContentsOfFile:vFile
set pList to current application's NSPropertyListSerialization's propertyListWithData:theData options:0 format:(current application's kCFPropertyListXMLFormat_v1_0) |error|:(missing value)
set bmData to pList's objectForKey:pPrefItem
set theURL to current application's NSURL's URLByResolvingBookmarkData:bmData options:0 relativeToURL:(missing value) bookmarkDataIsStale:(missing value) |error|:(missing value)
return theURL's |path|() as text

At the moment after I use the delimiters to extract the base64-encoded bookmark between the <data> tags, I am passing the base64 bookmark to a handler I cobbled together based on code you provided me a few years ago:

my hDecodeBookmark(vPhotosLibraryBookmark)

on hDecodeBookmark(vBookmark)
	-- MacOS Bookmark resolver code adapted from <>
	-- Information on what MacOS Bookmarks are at <>
	set theData to current application's NSData's alloc()'s initWithBase64EncodedString:vBookmark options:(current application's NSDataBase64DecodingIgnoreUnknownCharacters)
	set theResult to current application's |NSURL|'s resourceValuesForKeys:{"NSURLBookmarkOriginalRelativePathKey"} fromBookmarkData:theData
	set theResult to NSURLBookmarkOriginalRelativePathKey of (theResult as record) -- if you want a record instead of a dictionary
	return POSIX file ("/" & theResult) as alias
end hDecodeBookmark

I don’t really understand ASObjC, but one thing I notice in that code is that it’s specifically taking and decoding base64 text using NSData’s initWithBase64EncodedString. Would it be possible to modify this handler to expect binary data instead of base64 text? If so, I could probably then use:

set vPhotosLibraryBookmark to value of property list item "IPXDefaultLibraryURLBookmark" of vFile -- vPhotosLibraryBookmark will be a binary data type
my hDecodeBookmark(vPhotosLibraryBookmark) -- revised handler which expects «data» instead of base64 «text»

You can probably use initWithBase64EncodedData:, passing AppleScript data.

When I change initWithBase64EncodedString to initWithBase64EncodedData in the handler and pass the value of property list item I get an AppleScript error:

-[NSAppleEventDescriptor length]: unrecognized selector sent to instance 0x6000018906c0

Looks like you’re out of luck with that approach. Just rework your code to use something like what I posted above – it will be a lot quicker than using System Events anyway.

1 Like

Thanks for the confirmation and your help, much appreciated.