Error updating plist file

I have an app which has a preferences section. The preferences( Property, Value) are displayed in a table view from a CUSTOM plist file. A user can double click a table row which then populates two textfields ‘Property’ and ‘Value’ respectively. A user can then update the value for a particular property and submit it.
After successful write to the plist file, the preferences are updated and displayed in a table. The issue is that every now and then I get an error when updating the plist file. Here is the code that updates the plist file:

on updateP_(sender)
set pval to theVal as string
set pkey to theProperty as string

// theVal and theProperty are the text field bindings for the Value and Property textfields

			do shell script "defaults write " & quoted form of plistFile & " " & quoted form of pkey & " " & quoted form of pval
			set updatedPref to {theProperty:pkey, theVal:pval}
			theDataSource's replaceObjectAtIndex_withObject_(doubleClickedSelectedRow as integer, updatedPref)
			aTableView's reloadData()
		on error errorMessage
			display dialog errorMessage
		end try
end updateP_

The code above works fine in some cases. But when I tried to submit a value “[Test String]” for a property, I got the following error:


quoted form of

take care of the quoting issue? Are the characters ‘[’ and ‘]’ the real issue?

I don’t know about “quoted form of”, but why are you using a shell script to write your preferences instead of using NSUserDefaults?

I’m relatively new to Applescript and Xcode and still learning the ropes. I didn’t know about NSUserDefaults. I have been using shell script to write to plist files and it has always worked for me until I hit this road block.
The plist file is created by my app automatically for every user when the app is run the first time

You should have a look at the NSUserDefaults Class Reference in the docs. There is a method, registerDefaults which essentially set the “factory defaults” which show up the first time an app is run, and then the user can change those with setValueForKey, and then those new defaults will show up until they’re changed. You can also bind the values of a text field to the Shared User Defaults Controller in IB, which I think will do what you want with no code at all.


THis turned out to be a quoting issue in bash. Here is the solution that worked:

do shell script "defaults write " & quoted form of plistFile & " '" & pkey & "' " & " '\"" & pval & "\"' "

Still, NSUserDefaults is way more easier and does a lot of the work for you. Also, in 10.7, the Preferences folder is now owned by the OS, which means your script may not work anymore. NSUserDefaults will be the only way. Here’s a start. Put this in your app delegate:

First you need to define a property to the user defaults before using it:

set theDefaultManager to current application's NSFileManager's defaultManager()

This goes in the applicationWillFinishLaunching_ method:

on registerAppDefaults_(sender)
		tell userDefaults
			registerDefaults_({preferenceOne:"value", preferenceOne:"value"})
		end tell
	on error theError number theErrorNumber
		log ("registerAppDefaults_ : " & theErrorNumber & " : " & theError) as string
	end try
end registerAppDefaults_

To read them back in, again in the applicationWillFinishLaunching_ method:

on readDefaults_(sender)
	tell userDefaults to set preferenceOne to (stringForKey_("preferenceOne")) as string
end readDefaults_

And to save them when you app terminates you put this in the applicationShouldTerminate_ method:

on saveAppDefaults_(sender)
	tell userDefaults to setObject_forKey_(preferenceOne, "preferenceOne")
	tell userDefaults to |synchronize|()
end saveAppDefaults_

Plus you could also save the entire contents of the table view in the preferences by using bindings in IB. No code, just binding the contents of the array controller with Shared User Defaults and assigning a path key to the name you’d like the preferences to be saved, and all is done automatically.

Browser: Safari 531.22.7
Operating System: Mac OS X (10.6)

I noticed that in your examples, the property theDefaultManager was not used. When I use NSUserDefaults I use

property userDefaults : class "User Defaults" of current application

and then I use your script.


Sorry, made a mistake. :slight_smile: It should have been this instead:

tell current application's NSUserDefaults to set userDefaults to standardUserDefaults()

and that goes in the applicationWillFinishLaunching_ method, before the registerAppDefaults_ and readDefaults_ of course. userDefaults is defined as a global property on top, anywhere after property parent : class “NSObject”.

But I guess your way could work too, haven’t tried it. I kinda remember havin’ seen it used like that before, but not 100% sure… but hey, I guess it doesn’t hurt to try! :slight_smile:

Browser: Safari 531.22.7
Operating System: Mac OS X (10.6)

and that should be

property userDefaults : class "NSUserDefaults" of current application

for it to work. You’re accessing a cocoa framework, gotta use the correct spelling. :wink:

Browser: Safari 531.22.7
Operating System: Mac OS X (10.6)

property userDefaults : class "NSUserDefaults" of current application

That is what it should be. :slight_smile:

Sorry about that.

I just found by accident what I saw long ago:

At the end, last section:

property NSColor: class "NSColor"
property NSCalibratedRGBColorSpace: my NSCalibratedRGBColorSpace
script MyView
   property parent: class "NSView"
   on drawRect_(rect)
       set c to NSColor's whiteColor()
       c's colorUsingColorSpaceName_(NSCalibratedRGBColorSpace)
   end drawRect_ --fixed Apple's mistake, was end convert_ ?!?
end script

so no “of current application” it seems. Untested though…

Browser: Safari 531.22.7
Operating System: Mac OS X (10.6)

The problem comes when you actually try to compile such a thing…