Details
Nothing to say, yet
Big christmas sale
Premium Access 35% OFF
Nothing to say, yet
The speaker is announcing a special edition podcast about Widget.System, a powerful tool for widget builders. They give examples of how Widget.System can be used, such as copying text to the pasteboard and grabbing version numbers from files. They explain that Widget.System is a JavaScript object with properties and methods, and it can be used by making system calls. They provide instructions on how to use Widget.System in a real-world code example. They also discuss the different modes of operation, synchronous and asynchronous, and recommend using asynchronous mode. They explain how Widget.System can access various resources on the user's system, such as AppleScripts, PHP, Ruby, Python, Unix commands, and shell scripts. September 18th, 2006, special edition, Welcome to the Flipside with Jon Brown. Hello everyone, how are you doing today? I hope everyone is great. I just wanted to let you guys know about today's special edition podcast. Now, I'm not scheduled to be putting on a podcast until this upcoming weekend, but I wanted to put this out a little bit earlier because Andrew Hedges went through a lot of trouble to create a really great audio tutorial on Widget.System. Widget.System allows you to call any script from within your widget or call any command line command using the Unix or Terminal. So, again, really powerful. I really wanted to get this out to you as fast as possible. I put a post on the blog as well as a whole page dedicated to the tutorial that he created. So, I hope you guys enjoy the tutorial by Andrew Hedges with Widget.System. If you have any comments about the tutorial, you can head over to the show blog at WidgetShow.com or send your audio comments to WidgetShow at gmail.com. Enjoy. Thanks, John. As a widget builder, one of the most powerful tools at your disposal is Widget.System. With it, you can access nearly any resource on your user's system including Unix commands and scripts, Apple scripts, or even scripts written in PHP, Ruby, Python, or even C. A common example is copying text to the pasteboard. I do this in my password generation widget, Make a Pass, available at andrew.hedges.name.widget if you want to see an example. With Widget.System, Make a Pass users can click the copy button and have the generated password copied to the pasteboard for later use. The JavaScript is basically one line that calls two different Unix utilities, echo and pbcopy, to do the work. Another example developers should find useful is this. In my latest widget, Sundial, available at clearwired.com slash sundial, I use a tip I saw on Apple's dashboard dev mailing list to grab the version number from Sundial's info.plist file. I then compare that number with one I retrieved from our server to let the user know whether a new version of the widget is available. It's a really slick solution to an issue I've heard voiced time and again, and it makes versioning your widget simpler because you only have to keep the version number in one file. The last example from Apple's dashboard tutorial is Making a Widget Speak. Their example consists of passing the AppleScript command Save along with some user-generated text to AppleScript via Widget.System. As you can see, this bit of code is no one-trick pony. Widget.System opens up countless possibilities for advanced widget functionality. So what exactly is Widget.System? In the dashboard environment, Mac OS X gives you access to a special JavaScript object called Widget. Like any JavaScript object, Widget has several properties and methods. One of Widget's most powerful methods is the system call that we're going to talk about here. For more information on other properties and methods on Widget objects, visit Apple's excellent dashboard documentation on developer.apple.com. So how do you use it? I think the best way to explain the use of Widget.System is by working through some real-world code. I'm going to use my PhpFunctionReference widget as our example. So if you want to follow along, pause the podcast now and go get it at app.andrew.hedges.name/.widgets. Unzip the download, right-click on the widget bundle, and choose Show Package Content. Then fire up your favorite text editor. That's where we'll pick up in a few seconds. Okay, welcome back. For those of you following along at home, you should be looking at a folder containing the files for PhpFunctionReference. Now, in order to use Widget.System, the first thing you need to do is make sure your info.plist file includes the key allowSystem with a value of true. This gives your widget access to the system method of the widget object. Open info.plist, and you'll see how this is coded. Then open key tag, the string allowSystem, all one word, and a closing key tag, followed by a self-closing true tag. When you're developing widgets, sometimes you want to do things that are easier in a scripting language or just not possible using JavaScript alone. In PhpFunctionReference, I use PhpScripts called from Widget.Systems for two important tasks. One, determining the language versions of the Php documentation that are installed, and two, parsing the documentation files themselves using regular expressions to grab what's between the body tags. I could have done the parsing in JavaScript, but I found it more reliable in Php. To determine the installed languages, I wanted to read the contents of a directory, something that, as far as I know, would have been impossible using JavaScript alone. Let's look more closely at the routine for determining the installed languages. There are two modes available with Widget.Systems, synchronous and asynchronous. Synchronous operation means your JavaScript sits and waits until it gets a response from the system. One implication of this is that your user system could potentially hang if something goes wrong with your Widget.System call. Asynchronous mode means your Widget continues to operate while it waits for the system to respond. Apple recommends always using asynchronous mode. I do still use synchronous mode for very simple operations like copying text to the page board, but use asynchronous for everything else. A Widget.System call consists of the text Widget.Systems followed by two arguments in parentheses. The first argument is a command you want to execute. This can be pretty much any command you might enter in the terminal. You can call system utilities such as pbCopy, or you can call, as we'll see in our Php function reference example, the Php binary and pass the file name to it as an argument. The second argument for Widget.Systems is a reference to a special function called a handler. In case you're not familiar with the concept, the handler is a function that is called when the current operation is complete. This is a common programming pattern, a common example of which are AJAX calls. When you make an AJAX request using, for instance, the excellent prototype.js library, a function is assigned to handle whatever is returned by the server. Widget.System works the same way. When the system finishes processing your request, it will pass some information back to your handler function. A quick tip here. The second argument should only consist of the name of the function. That means don't include parentheses like you do when you're defining the function, because that will cause the function to execute at the same time the system command is called, which is probably not what you want to have happen. If you specify the second argument as null, you'll be working in synchronous mode. Since this is not the recommended way of doing things, I'm not going to spend time explaining the differences in how these two modes work. If at all possible, just write your Widget.System calls asynchronous right from the start. Okay. When php function reference first loads, it looks in the php underscore manual folder for any subfolders in order to know what languages are installed. By default, there's a folder there that's called en for English. It does this by making a Widget.System call consisting of calling php from the command line and passing to it the path to a php script contained within the widget that reads the contents of the directory and returns a string containing the names of the subfolders. Let's look at the Widget.System call itself. Open up the scripts folder and open the file phpfr.js. Jump down to line 378 and you'll see a function called getInstalledLang, short for languages. This function consists of one line, our Widget.System call. Quick aside, you'll see that I actually call wdw.Widget.System. That's because earlier in my script I set wdw to equal window object because calling window.Widget.System is a teeny tiny bit faster than just calling Widget.System. I can hear my co-worker Daniel now groaning something about premature optimization. Our Widget.System call has, of course, two arguments. The first calls php from the command line and passes to it the path to a php script contained within the widget. The second argument is the name of our handler function setInstalledLang. Next, we'll look at our php script, installed.php. Like phpfr.js, installed.php is located in the scripts folder. Open it up and you'll see a simple php script that opens the php underscore manual directory, loops through its content, and adds the names of the enclosed folders to a string. This string is then written to standard output, effectively sending it back to our Widget.System call. Note that even though phpfr.js and installed.php are both located in the scripts folder, our Widget.System call references the path relative to the root of the widget, not the location of the JavaScript file, so it calls script slash installed.php to call the php script. When the system call completes, it passes the JavaScript object to our setInstalledLang's handler function. This object has several properties, one of which is outputString. In this case, outputString consists of a text written to standard out by our php script. The routine completes by parsing the string passed into setInstalledLang and saving the contents to a variable that is accessed by other parts of the script. To summarize, with allowSystemSetToTrue in your info.plist file, Widget.System can access pretty much anything on your system that has a command line interface. AppleScripts, PHP, Ruby, Python, Unix commands, shell scripts, etc, etc, ad nauseum. Widget.System can run in two modes, synchronous or asynchronous, though only asynchronous is recommended by Apple. Asynchronous mode requires that you write a handler function that will receive a JavaScript object with several useful properties, including outputString. Armed with this knowledge, the limitations of a typical web page fall away, allowing you to do some pretty cool things within the Widget framework. In an unrelated tip, I highly recommend using skedit as your text editor for widget development. It's got some great features like snippets, but the thing that sets it apart from widget development in particular is that you can set up your system's widgets folder as a project and have easy access to the inners of all of your widgets in a tiny tabbed interface. I use it constantly to reference codes from my previous widgets or to gain inspiration from the work of others. It's only $25 for a lifetime license. More information is available at skti.org. Well, that's it. I hope folks found this helpful. For more information, the definitive source of information about all widget development topics is developer.apple.com. Back to you, John. Thanks, Andrew. Again, it was a really, really great tutorial. Thank you very much for putting it together. Widget.System is very powerful and something that every widget developer really should start dabbling in. It's a really great way to add full functionality to your widgets. If you want to find out more, like Andrew said, head over to developer.apple.com. Also, if you want to learn more about Andrew's widgets, you can head over to his website at andrew.hedges.name.widgets. Or, if you have an audio comment for the show, send it to widgetshow.gmail.com. Thanks for listening to this special edition of the podcast. I really hope you guys enjoyed it, and we'll catch you guys all next time on the flip side. .