user defaults

Hi All,

I am trying to make sense out of the cocoa information on User defaults. If anyone can steer me in the right direction…

Previously I used “Tell User defaults” etc. in ASS apps.

Now in ASOC I am using the bindings for my UI elements so they can be ‘set’ and “get” simply from script. This is truly great. But I notice you can not bind an element to its property and also to Shared User defaults. I may be wrong.

So what are my options? I thought of using Craigs’ booklist example and creating an NSMutableArray object and writing one long record to file, containing all the properties of the UI and also possible another array for a table view…

But I want these user settings to reside in the shared User defaults plist in the user Library Preferences, rather than a separate file somewhere.

Any direction here will help and i will continue research.

Has any one heard about examples coming from apple any time soon? They said 'in a week."

Thanks,
Rob D

Ok so I set up a test array and write to file:


property Newarraydata : {{}}
property FILE_PATH : POSIX path of ((path to desktop as string) & "ImageApp.plist") 
-- how to get path to bundle resources? "resource path of main bundle no longer works...

--bindings to check boxes
property flipit:0
property rotateit:0
property scaleit:0

Set up the array at launch or read in from file.


on awakeFromNib()
		set Newarraydata to NSMutableArray's alloc()'s initWithContentsOfFile_(FILE_PATH)
		if Newarraydata is equal to missing value then
			set Newarraydata to NSMutableArray's alloc()'s init()
			set arraySettings to {{flipit:flipit, rotateit:rotateit, scaleit:scaleit}}
			Newarraydata's addObjectsFromArray_(arraySettings)
		end if
		my readFile()
	end awakeFromNib

at close:

The file shows the correct values according to the UI checkboxes states after quitting.

on writeToFile()
(Newarraydata’s setValue_forKey_(flipit, “flipit”))
if not Newarraydata’s writeToFile_atomically_(FILE_PATH, true) then
set messageText to “There was an error writing to file”
display dialog messageText buttons {“Ok”} default button 1
end if
end writeToFile

but then when I read it back in the value is captured for “flipit” but the UI does not update to the new value.


on readFile()
set flipit to Newarraydata's valueForKey_("flipit")
display dialog "" & flipit & ""  -- shows correct value but UI doesn't update. 
end readFile

Doing it this way though, I still am not using the user defaults plist and have to write out, and read in, every property bound to a Ui element. Just like the old days with “Tell User Defaults…”

Rob D

I relaized my error with retrieving the checkbox value:


set thisObject to Newarraydata's objectAtIndex_(0)
setFlipit_(thisObject's valueForKey_("flipit"))

I figured since the array had just one object (containing several key value pairs) I could access the key directly. Not!

But I am still stuck with reading in and out 23 UI properties ( My sample “Newarraydata” has only a few.)

in search of User defaults, Rob

hi All,
Sorry to answer my own questions…

I found out how to write to user defaults very simply. It isn’t really much different from the old “Tell User Defaults” in ASS.

–at the top

property NSUserDefaults : class "NSUserDefaults" of current application

to write to user defaults;

tell NSUserDefaults
	tell its standardUserDefaults()
		its setObject_forKey_(flipit, "flipit")
		its setObject_forKey_(rotateit, "rotateit")
		its setObject_forKey_(scaleit, "scaleit")
	end tell
end tell

to read back in:

tell NSUserDefaults
	tell its standardUserDefaults()
		set flipit to its objectForKey_("flipit")
		my setFlipit_(flipit)
		set rotateit to its objectForKey_("rotateit")
		my setRotateit_(rotateit)
		set scaleit to its objectForKey_("scaleit")
		my setScaleit_(scaleit)
	end tell
end tell

There are more elegant way perhaps…

NSUserDefaults's standardUserDefaults()'s setObject_forKey_(flipit, "flipit")

You can also set the defaults on first run if no defaults exist yet with :

From http://developer.apple.com/mac/library/documentation/Cocoa/Conceptual/UserDefaults/UserDefaults.pdf


+ (void)initialize{
}

NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; 
NSDictionary *appDefaults = [NSDictionary
dictionaryWithObject:@"YES" forKey:@"DeleteBackup"]; 
[defaults registerDefaults:appDefaults];

getting there slowly. Any feedback or refinements appreciated,

Rob

One thing to watch out for: you use setObject_forKey_ for text properties, but if the properties are booleans or numbers, you have to use a different method (setBool_forKey_, etc).

Oh - thanks Shane,

I somehow missed that.

just like

“set contents of default entry “flipit” to flipit as boolean.”

Same stuff being said as in ASS I guess.

What about storing an array (list of records) in NSUserDefaults?

would setObject_forKey cover that?

I see there are many more getting specifiers than setting ones.

There is an arrayForKey but no setArray_forKey.

Rob

Yes, setObject_forKey_ works for lists (arrays) and records (dictionaries), but getting them back involves different methods. Best to look up NSUserDefaults in Xcode → Help → Developer Documentation.

Hi Shane,

I just tried changing objectForKey to boolForKey and setObject_forKey to setBool_forKey but the defaults are not retained. These are for checkboxes which definitely show true and false values and have checkboxes for values in the defaults plist file.

Go figure. objectForKey works for both the booleans and the strings. Just out of curiosity I tried Integer but no go.

Rob

Hi all,

I am almost finished my app, and must say it has gone very well after getting through the challenges with my PathControl popups that Shane helped me with in this post :http://macscripter.net/viewtopic.php?id=30644. Surprisingly the app runs smoothly and seems to be bug free. :slight_smile: It has 4 user selections and based on user input, I use a repeating NSTimer to process the user request after it’ fires… includes a countdown timer with updates to the window every second and a log file that annotates all events including user setting changes. Even lets the user change certain settings during the countdown period on the fly and resets the timer, then restarts the timer . And finaly uses what I learned from Shane’s Bindings tutorial to disable the critical controls during processing ! Thanks Shane, I was even able to incorporate a Pause button as an extension of the ideas in that tutorial!

Very pleased with what I’ve been able to do so far in ASOC, considering I have only coded in AS and never touched AS Studio before beginning this journey.

Now that it is almost ready for prime time all I need to do is save the prefs which are simple in this case.

I now have 2 path controls and 2 popup menus, all of which have null placeholders in them for first run.
Also I have the HFS paths that have been converted from each of the pathcontrols as well as 2 other properties used in the code. Again nothing to be dome on first run.

The problem seems to be saving the pathcontrols using user defaults and being able to read them back to the window after the user selects a new value and quits, then restarts.

I have taken every tutorial available from both the Studio forums, and checked the OBJC stuff out from Apple (which confued me more lol) , and read all the documentation, but nothing seems to be sticking regarding the pathcontrols and the variables, so I am lost on this final part of the app.

This would be a great subject for a complete tutorial using complex controls and their values I think. As I understand it I need not only to restore the controls but also their values which could be 2 different functions?

Could anyone point me in the right direction so I can using applescript coding :

  1. set up the default properties internally on first run (Not really needed if I don’t set my properties to missing values)
  2. save the values for the 4 elements upon user selection using bindings
  3. save everything on quit including those pathcontrols (if required, bindings have not worked for me so far on this)
  4. get all the values back into my variables/properties on startup including those 2 path controls

Thanks much for any assistance,
Jim

You should be able to bind the values of the path controls to user defaults, enter suitable keys for them, and leave it at that. That assumes you’re no setting their values via bindings already. But it’s hard to say any more without seeing what you’re actually doing in terms of code and interface.

That was it. I was in fact over complicating things. Thank you shane, your answer prompted me to rethink what I was trying to accomplish here lol. The 2 pathControls were bound to user defaults with no issues. The 2 popup buttons’ values were previously wired to properties so I just bound them to user defaults and set the properties from the user defaults instance using the read-in methods above. Works perfectly with only one issue left.

I found the old Terminal commands for writing to user defaults and in fact they still work once placed in my script. Testing them I tried to update the code to ASOC code after reading the turorial and class reference, but found robdut’s to be the best solution, except of course nothing is happening on launch re: the values from properties I wrote into the user defaults i.e. initializing everything properly on launch, writing first, then reading the file with no values being overwritten, as explained in the docs.

The way I was doing this with the old terminal commands in Applescript (from tutorials I had taken) was something like this (with variables):
do shell script (“defaults write " & myDomain & " " & (the quoted form of theKey) & " -” & theType & " " & (the quoted form of theValue) as text) … and if the property was already there it wasn’t changed, and the defaults read command would set everything up for me. This still works but is very old so I would like to keep as much to ASOC as possible.

I learned that in AS Studio the initialize command was like this:
make new default entry at end of default entries of user defaults with properties {name:“defaultName”, contents:“Testing”} from the docs of course …but that doesn’t work in ASOC.

The code above in this thread for initialization is in OBJC and I would prefer to remain ASOC as much as possible, but just have a hard time constructing (guessing ) the AS version of OBJC, even after the tutorial #5 on the forum for ASOC.

Any idea how to put the tell statement together for the initialize User defaults method in ASOC with 2 internal variables I need to read and write from/to preferences, in my on “applicationWillFinishLaunching” handler?

Property theFrequency: “100”
Property theAge: “45”

Many thanks !

If I’m understanding you correctly, you want some way to set the factory defaults that will be used in the absence of defaults saved to a file when the app last ran. If so, you need something like (untested):

		set prefs to {theFrequency:"100", theAge:"45"}
		set userDefaults to current application's class "NSUserDefaults"'s standardUserDefaults()
		userDefaults's registerDefaults_(prefs)

Such prefs are not saved to disk, and are overridden by an app-domain defaults read from disk.

Thank you Shane, that worked perfectly. It started the app with the defaults, and when I changed the first value the file was written to the preferences folder. I only used the set, get calls above and the initialization method in your post. As I said above I was overcomplicating things a bit, but using only the 2 properties above let me get my head wrapped around all the automation that bindings and the NSuserdefaults class offers.

My app is done! All in ASOC, and the only non-applescript code was getting the url’s from the path controls and this preference code. Just preparing an icon, and it’s ready for prime time!

Thanks for your assistance once again.

Hi All,

I have a few problems with writing to user defaults:
what do I have to write exactly into the “setObject_forKey_” and the “objectForKey_” parts?

In my application I’m using some text fields and I want to save their values as string.

Example:

property thistextinput_field : missing value //this is connected to a textfield in the IB

//for working with the value I have this two lines:

set usertext to thistextinput_field’s stringValue()
set theusertext to usertext as string

//now the user has to write some text into the text field and after relaunching the application the text should still be in the text field

→ so the question:

What do I have to write in the parts I marked with the questions marks???

//for writing the user defaults

tell NSUserDefaults
tell its standardUserDefaults()
its setObject_forKey_(???, ???)
end tell
end tell

//for reading the user defaults

tell NSUserDefaults
tell its standardUserDefaults()
set ??? to its objectForKey_(“???”)
my setFlipit_(flipit) //don’t unterstand that line???
end tell
end tell

Thanks!

Hi Borhahn,

You might be better of using capitals to delineate variable names instead of underscores since the mthods use underscores.

so:

set usertext to thistextinput_field’s stringValue()

could be

set usertext to thisTextInputFeld’s stringValue()

then to read in:

on awakeFromNib()
		tell NSUserDefaults
			tell its standardUserDefaults()
				set thisTextInputFeld to its stringForKey_("thisTextInputFeld")
				my setThisTextInputFeld_(thisTextInputFeld)   --this sets your text field's value
			end tell
		end tell
end awakeFromNib

the line

my setThisTextInputFeld_(thisTextInputFeld)

is an accessor method that is built in for your property declared at the top (it simple adds “set” to variable name)

so the “getter” would be

thisTextInputFeld’s stringValue()

and the “setter” would be

setThisTextInputFeld_(thisTextInputFeld) --note the capitalized “This” in your variable name.

then to write out…

on writeToFile()
		tell NSUserDefaults
			tell its standardUserDefaults()
				its setObject_forKey_(thisTextInputFeld, "thisTextInputFeld")
			end tell
		end tell
end writeToFile

You can use the setObject_forKey_ form for writing but should be more specific about reading in with

stringForKey_(“thisTextInputFeld”)

The defaults use standard key value pairs so you have the value followed by the key

(thisTextInputFeld, “thisTextInputFeld”)

you could use different names for the key of course

(thisTextInputFeld, “textFieldKey”)

Hope that helps.

Rob

Thanks! That helped a lot! :slight_smile:

Ok I need help understanding these new defaults. I have a property I want to save which is “thepass” and thepass is set to whatever the user set the password as when he first opened the application. Now I need to save this password and be able to reload the pass on startup next time. I really do not under stand any of the other stuff in this post, could someone show me how this would be written?

Really, everything you need is in this thread; it’s pointless for someone to type it all in again. Re-read – the code you need is covered in message #4.

Hi,

I’m (very) new to ASOC and currently struggling trying to implement the code described above to work with my application. I did quite some trial-and-error but I just can’t get the script to save or read the user defaults. I’m guessing my problem is probably a misconception… Or I overlooked something. Either way, I would be very glad if someone here could point me into the right direction.

Here’s the relevant parts of my script. Everything except for the preferences part works as expected. Basically, there’s just 4 text fields containint strings in my app’s window which I’d like to include in the preferences file.

My understanding: Load the preferences on initialisation, save the preferences on exit.



script drupdaterAppDelegate
	
	property NSUserDefaults : class "NSUserDefaults" of current application
	
	property statusLogWindow : missing value
	property testServer : missing value
	property modulesOnly : missing value
	property safeUpdate : missing value
	property yftpBookmarkFolder : missing value
	property localDrupalFolder : missing value
	property localBackupFolder : missing value
	property tokenString : missing value
	
	global statusLog, currentDate, curlBrowserString
	
	
	on applicationWillFinishLaunching_(aNotification)
		-- Insert code here to initialize your application before any files are opened 		
				
		-- set defaults
		set prefs to {tokenString:"blabla", localBackupFolder:"blablabla"}
		set userDefaults to current application's class "NSUserDefaults"'s standardUserDefaults()
		userDefaults's registerDefaults_(prefs)
		
		-- read prefs
		tell NSUserDefaults
			tell its standardUserDefaults()
				set yftpBookmarkFolder to its objectForKey_("yftpBookmarkFolder")
				my setYftpBookmarkFolder_(yftpBookmarkFolder)
				set localDrupalFolder to its objectForKey_("localDrupalFolder")
				my setLocalDrupalFolder_(localDrupalFolder)
				set localBackupFolder to its objectForKey_("localBackupFolder")
				my setLocalBackupFolder_(localBackupFolder)
				set tokenString to its objectForKey_("tokenString")
				my setTokenString_(tokenString)
			end tell
		end tell
		
	end applicationWillFinishLaunching_

(*

all the other stuff

*)


	on applicationShouldTerminateAfterLastWindowClosed_(sender)
		return true
	end applicationShouldTerminateAfterLastWindowClosed_

	on applicationShouldTerminate_(sender)
		-- Insert code here to do any housekeeping before your application quits 
		tell NSUserDefaults
			tell its standardUserDefaults()
				its setObject_forKey_(yftpBookmarkFolder, "yftpBookmarkFolder")
				its setObject_forKey_(localDrupalFolder, "localDrupalFolder")
				its setObject_forKey_(localBackupFolder, "localBackupFolder")
				its setObject_forKey_(tokenString, "tokenString")
			end tell
		end tell
		return my NSTerminateNow
	end applicationShouldTerminate_
	
end script


What am I missing? :frowning: I get the following errors on console:

On initialisation:

On exit:


I’m stuck… :stuck_out_tongue: Thanks for any inputs!

Regards,

Oliver

Could it be that the variables you’re trying to save are references to text fields, rather than containing the text in the fields?