One more of those user defaults questions

I read through all of the posts here on user defaults and one asked about saving a dictionary.

Here’s what my table view looks like in the form that I would put it as a property:

property players:  {{"Doc", {}, ""}, {"Grumpy", {}, ""}, {"Sneezy", {}, ""}, {"Sleepy", {}, ""}, {"Happy", {}, ""}, {"Bashful", {}, ""}, {"Dopey", {}, ""}


So it’s pretty simple, seven (or more) rows containing a name, a list of strings, and a string.

I made a property list manually and it looks like this as output of “defaults read” in Terminal:

“Player 1” = {
Name = Grumpy;
VotedFor = Dopey;
VotesAgainst = (
“Player 2” = {
Name = Dopey;
VotedFor = Doc;
VotesAgainst = (
“Player 3” = {
Name = Sneezy;
VotedFor = Bashful;
VotesAgainst = (

I tried the following code to save it as user defaults:

to saveData()
	tell theDataSource
		repeat with theRow in data rows
			tell theRow
				make new default entry at end of default entries of user defaults with properties {name:contents of data cell "playernames", votedFor:contents of data cell "currentVotedPlayer", VotesAgainst:contents of data cell "votersFor"}
			end tell
		end repeat
	end tell
end saveData

Of course it’s giving me a runtime error.

Will user defaults take a dictionary like this and read and write it properly? I saw the example with the Beatles and that is an array but this dictionary has one more level.

I hate to have to script a custom data format just to save and load a small dictionary.

Can anyone tell me if I am on the right track? Will user defaults save and load this kind of object? Maybe I am screwing up something by trying to write it a row at a time. I’m not really sure what a user defaults entry is supposed to correspond to when you have arrays within a record.

Any help appreciated.

Not to detract from the user defaults question, but it should be possible to use store script and load script to write and read your variable.

The relevant properites that the [url=]default entry[/url] class has are content, contents, and name; Anything else (i.e. votedFor or VotesAgainst) wouldn’t make any sense.


I’ll give that a shot. Never thought of that. I’ve heard of store script but never used it in all these years.

Hopefully it will go into the app bundle.

Failing that, I suppose I could set the “contents” of a default entry to be the whole list of lists. Then reading it back in hopefully setting the contents of the row to the contents of the default entry would work.

Thanks for the tips.

Well, I just thought I would update.

user defaults did nothing for me - it would only save the first element of a list that was in another list, so I bailed on that.

I installed Late Night Software’s “Property List Tools” osax, and here are the code snippets I used to easily get data from the data source as a record, save the record as a property list, reload the data from the property list, and set the data source to the loaded data:

Saving: (I made a handler because saveData() is called from three places)

set playerList to contents of data source "Players" -- returns a record, which is perfect.
tell me to saveData(playerList)

where the saveData handler is

to saveData(playerList)
	store property list playerList in (path to resource "com.jlsoftware.mafia.plist") -- in "Resources" of app bundle
end saveData


set playerList to my loadData()

whose handler is

to loadData()
	set playerList to read property list (path to resource "com.jlsoftware.mafia.plist")
	return playerList
end loadData

Setup steps for an Applescript Studio app with this feature:

  1. Get Late Night Software’s “Property List Tools” osax from
  2. Move the osax scripting addition bundle anywhere; I put it in ~/Library/Scripting Additions but for ASS apps it doesn’t matter
  3. In Xcode, right-click on the Target and add the osax file to the target.
  4. Add a copy files step to copy the osax into the executable.
  5. Put a blank plist file on the Desktop so Xcode can copy it.
  6. Add a Copy Files step so Xcode will copy the fresh plist file into the bundle on every build.

Right now, the saving and loading works great - the resulting plist file even has labels.

The only thing I need to fix in this version is the following:

I have an “Edit” button on the window which just sets a column of a table to editable. The user then can double-click the data cells in that column to change the users to different users. That works, but I want them to be able to fill out the whole column by hitting Return or Enter and having the next row become ready for typing. At present, you have to double-click each row to enable it for typing in it.

Anyway, just wanted to pass this info along and wish everyone a happy holiday season.

Now that my app is working, I discovered that if I move it to a machine that does not have the osax in the ScriptingAdditions folder in ~/Library, the script will not compile. Well, that makes sense, so I read some mailing list entries on this and it appears that putting the osax into /Contents/Resources/Scripting Additions of the completed app bundle should work.

However, the LNS Property List Tools, when I put the osax in Resources, compiles (i.e. it knows the dictionary and the osax’s terms turn blue), but the script bombs saying the plist file can’t understand the message from the osax.

So it appears that the script is finding the osax (I removed it from Library/ScriptingAdditions and logged out and in), and compiling correctly, but the osax isn’t working unless it is in the usual Library folder.

Am I stuck having to have each user install the osax in their Library?

If so, and if LNS can’t fix it to run out of the app bundle, what is the way to use Load Script to save and load a record? The record is a list of lists with example

{{1, {2,3,4}, “5”}, {6, {7,8,9}, “10”}} maximum 7 sublists.

An end to the story:

I trashed the osax because it would not work unless it was placed in the users’s Library.

I found a post on the mailing list that suggested to use call method of saving and loading files from an array as a plist.

set thePlistPath to path to resource "com.jlsoftware.mafia.plist"
tell me to writePropertyList(thePlistPath)
to readPropertyList(thePath)
	return call method "arrayWithContentsOfFile:" of class "NSMutableArray" with parameter (thePath)
end readPropertyList

to writePropertyList(thePath)
	return call method "writeToFile:atomically:" of (contents of data source "Players") with parameters {thePath, true}
end writePropertyList

I think this is a good way to go. It works, there is no osax to mess with, and it handles any elements that it can interpret as a plist element.