OK, let me try a festive offering. This is actually from the third edition of 'Everyday AppleScriptObjC', and if you want the complete explanation, you'll need to buy the book. (Did I mention it makes a great stocking-stuffer?)
The situation is that AppleScript's traditional way of storing preferences, using persistent properties, is not always available. You can't use rely on it if you're codesigning for Gatekeeper compatibility, or if you're using GUI script, or if you have top-level Cocoa objects in your scripts.
Of course you can save values in files where and how you like, but this way uses the same system other apps use, with the values eventually stored in a property list file in ~/Library/Preferences. And because it uses this system, whereby all the file reading and writing is actually done by a separate process, it's very quick as well as very reliable. The only catch is that you can't use it for .scpt files: it's for applets or .scptd files only. (You should be able to use .scptd files instead of .scpt files in most, if not all, situations.)
So the process used by Cocoa apps is to talk to a thing called NSUSerDefaults, and to register what are called factory defaults. This is usually a record/dictionary of the values to be used for particular keys when no values have been set -- what you get straight from the factory, if you like. From then on you can get or set any value by its key, which is just some string you use as a label. Each key is roughly equivalent to a separate property.
AppleScript poses an extra wrinkle because although we might be running a script as an applet, when we're writing and testing it, it's actually being run by our chosen script editor, and we need to ensure that the values get written to the applet's prefs file, and not the editor's.
So a script would typically start like this:
use AppleScript version "2.3.1" -- yes, it supports 10.9 use scripting additions -- optional use defaultsLib : script "DefaultsLib" -- loads the lib property defaultsObj : missing value
This loads the lib, and declares a property that we can use to access stuff later on.
The next line is this:
if my id = missing value then error "This code works only in an applet or script bundle."
This is to stop accidentally storing values in your editor's preferences (something that's unlikely to be harmful, but is not a particularly useful thing to do). So you need to save your script as an applet or bundle (.scptd file) before you run it. Obviously you can leave this line out, but I think it's a good reminder.
Then next few lines are this:
if current application's id = my id then set theID to missing value else set theID to my id end if
As you probably know, preference files are named after the bundle id of the item concerned. The first line above is getting the id of the running app, and seeing if it's the same as the id of the script. It won't be when you're running in an editor (or from an app's script menu or panel), but it will be when you're running as a standalone app. The library needs to know which it is.
The next line calls a handler in the library that both creates a new defaults object, and sets the initial values:
set my defaultsObj to defaultsLib's makeDefaultsWithID:theID factoryValues:{property1:true, property2:2}
The names in the record can be whataver you like -- they're your labels. The values you can assign to them must be of classes that property lists can store: text, numbers, booleans, and lists and records containing the same. You can actually store other values, but you're going to have to buy the book or work that part out yourself.
So now when you want to retrieve the latest value for one of your keys, you address defaultsObj. For example, property1 above is going to be a boolean value, so:
set currProp1 to defaultsObj's boolForKey:"property1"
And property2 is going to be an integer, so:
set currProp2 to defaultsObj's integerForKey:"property2"
When you want to store a new value, you use:
defaultsObj's setBool:false forKey:"property1"
or: defaultsObj's setInteger:42 forKey:"property2"
You will see there are also special methods for storing and retrieving files, AppleScript dates, and other AppleScript values. You can even store aliases so they keep track of files when they move. Anyway, it fails Jon's simplicity test splendidly, probably fails Ed's easy-to-understand test, and unless you've already bought my book it fails the easy-to-find test, so it's future looks rather dim. But it addresses a growing problem in a robust and, dare I say it, almost elegant manner. (And did I mention there's more detail about it in my book, which makes a wonderful stocking-stuffer? Your chance to make me a little bit merry.) So much explanation, the script will have to come in a follow-up message... |