Tools • 1:07:18
Dashcode is a start-to-finish tool for building great Mac OS X Dashboard widgets in minutes. With elegant drag-and-drop design capabilities, reusable code snippets, and a powerful JavaScript debugger, Dashcode revolutionizes the widget development experience for beginners and experts alike. Get started with Dashcode and find out how to make the most of its design and debugging features. With these tools at your command, you'll be shipping polished Dashboard widgets in no time.
Speakers: Christian Wagner, Chris Ryan, Han-Ming Ong
Unlisted on Apple Developer site
Downloads from Apple
Transcript
This transcript was generated using Whisper, it has known transcription errors. We are working on an improved version.
Good morning and welcome to Friday at WWDC. I hope you're all enjoying your day and the rest of your time here. My name is Christian Wagner and I am the guy standing in front of you right now. So I hope you're excited for a good session. We're going to be talking about Dashboard widgets and iPhone web apps today.
So just a brief recap. Behind me, you'll see Mac OS X for Tiger. And one of the biggest features of Mac OS X for Tiger was Dashboard, which had these little widgets on them. And widgets are small applications built using web technologies like HTML, CSS, and JavaScript. So they were Ajax before Ajax was Ajax.
So it's one of those things that, you know, we brought these into Leopard then and added a couple more widgets. And we also added Dashcode, the IDE for creating Dashboard widgets. And Dashcode ships with 10 templates that make it very simple to start off your widget development. They come with all of the code and some design to kind of get you hit the ground running. Now, we're going to spend today's session talking about two things.
One, we're going to talk about taking a web application for the iPhone and bringing it onto Dashboard as a widget. Why would you want to do that? Well, you'd want to do that because this is a great way to take some existing work that you've already done and expose it to a whole new platform, the Mac, a whole new audience.
Being on the desktop gives you a whole new user base, people that you wouldn't normally be exposed to. And that's a great thing you can do with a small investment and some right kind of tips that we're going to give you in this session. And then after we talk about that, we're going to talk about taking your widget, if you're a widget developer, and bringing it onto the iPhone as an iPhone web application. We're going to do this for the same reasons.
It's a great way to kind of capitalize on some of the work that you've done making a widget and turning it into an iPhone web application. But first... We're going to go and make ourselves the eight-minute Twitter widget. That went really quick there. I'd like to bring my boss, Kirk Huddleston, on board. And Kirk here, well, we're going to do a little bit of a gamble. I said this is the eight-minute Twitter widget.
So Kirk here has his iPhone with the timer application, and I'm going to make this widget in eight minutes. If I don't, all of the wonderful people in this session are going to get Milky Ways and Hershey's. Just Halloween. Some more Milky Ways and Three Musketeers and Kit Kats. So. Woo.
So I'd like to go on to the demos, please. Kirk, are you ready? All right, and go. So we're going to start off here. This is your standard-issue Mac OS X 5 install, and I'm going to launch Dashcode. Dashcode, as you see here, has this new capability of doing web applications, but we're going to go down here and look at the Dashboard widgets. And I'm going to choose a custom widget. So Dashcode's user interface has this area on the left over here called the Navigator.
This is where you have your DOM tree and a bunch of other things that widgets need. Underneath that, you have steps that kind of guide you through the widget development process. And then over here in the main part of the window, you'll see that we have the Canvas. This is where you design your widgets. So I'm going to go about, I'm going to design my Twitter widget. So I'm going to reuse this Hello World text, drop it up at the top, and I'm going to call this Twitter-tastic.
You have to have Twitter in the name of your Twitter widget. And I'm going to keep this info button and move it up. So you'll see that in the canvas here. You know, it's literally drag-and-drop. You can move things around however you see fit. It's very visual. Then I'm going to bring up the library here. The library is where we have these Dashcode parts. These are prepackaged bits of functionality, styling code, all sorts of stuff that kind of jumpstart your widget development.
So I'm going to grab a part here called the scroll area. So the scroll area gives us a kind of, you know, content area. And if it overflows, it gives us a scroll bar. Okay? So I'm going to grab that and kind of size it like so. Now, one of the new things that we have in Dashcode 2 for you is a list part. The list part will take a kind of template row, and it will stamp out multiple instances. N instances of that row, however you set it up.
And then you provide some code to kind of shove the data in each row as you see fit. We'll get into that later. But right now, what I'm going to do is I'm going to take the template row here. I'm going to make it 48 pixels high. There's already one text field or text area in here.
I'm going to just take that, move it over here or so. I'm going to add a second one there. So the first one I'm going to use for the person's name that I'm going to be getting. The second one I'm going to use for their Twitter status. Okay? So I'm going to grab a text part.
You hurt? Grab this one, and we're just going to put that right underneath. And then I'm also going to grab, while I'm here, a box. So a box is just essentially, if you're savvy in the wonderful ways of HTML and CSS, it's just a div. Okay, but this is where I'm going to be putting people's user pictures.
Okay, so that's all I need from the library right now. I'm going to close the library. I'm going to bring up Dashcode's inspector. The inspector lets you inspect any item's properties. Okay, so you can add handlers, you can change its style, anything that involves that part, you can mess with that.
So what I'm going to do here is I'm going to turn off this box's stroke, because I don't need any line around it. There's going to be a picture in there. And then I'm going to turn on something new that we added in Dashcode to its design guide. I'm going to click away from the box.
You'll see that it just kind of disappears. Turning on the design guide just leaves a little bit of an outline there, so I know that, yes, there's something there. So I'm going to align that in the top here, and then I'm going to make it 48 by 48 pixels. I happen to know that Twitter user images are 48 by 48. And then while I'm here, I'm also going to say that I want this picture to be vertically aligned. And then I'm going to take the status.
I'm going to make it document flow. And what that means is it's going to actually push down, it's going to actually make the rows bigger as the status text grows. We'll get into that a little bit more later, too, but I'm going to make that about yay big. I'm going to make it grow on the inside. I'm going to make it document flow. I'm going to tell the template row to grow.
Last things I need to do around here right now is, I need to actually give each of these elements a unique ID that I can reference them from in JavaScript. So we're going to say the name should be the name. The second text field should be the status. And the box should be the picture.
So right now I've got a kind of bland-looking Twitter widget. Let's make it look a little bit snazzier. I'm going to take the three text bits here. I'm going to make them white and give them a black shadow. Now, of course, you can't see them. So I'm going to make the background of the widget solid black. And I'm going to make the background of the widget solid black.
And because it's not on the Mac, if it's not on the Mac, it's got to have glass. Basically, it's going to be on the Mac, so we'll add a glass effect here. We're going to take down the curvature, cut down on the shine a little bit. I'm going to move it up to about there. Okay, so that looks a little bit better. And just to make it fit in that much more in the Mac, we are going to make it a little bit opaque.
So that's cool. Now, the list is white, so let's just get rid of its background color. And that's our design. So let's go into code now. Now, if I swap out the steps here, you'll see I can get to the code that makes the widget. How are we doing on time, Kirk? I'm at five minutes. I got to do some three minutes.
[Transcript missing]
All right. That's pretty good. I'm going to run it. And...gah! Didn't work. All right. So what am I going to do? I'm going to look at this performance monitor here. Look at the performance monitor. You see that no network, nothing loaded over the network. So if I go back to the, uh, resource log, you'll see, and I go to network here, and I got this file, but this file, this JSON file doesn't have any information in it. And this is a common gotcha in widget development. I need to declare my intention to use network access. I click network access. I click stop. I'm going to run it now. Who thinks it's going to work now? Going to click run. And it works. Time.
7:54. Whoo! Thank you very much, Kirk. That said, stop by our lab this afternoon, have at the candy. You can have as much as you like, okay? Can I get back the slides, please? All right, so now that we've talked a little bit about making your first widget, I hope you've gotten a good introduction to how Dashcode works. I want to spend some time talking about taking your iPhone web application and making a widget, just some things that you should know.
So first and foremost, I want to talk about the idea of Model View Controller. Now, I'm sure you've heard all about this all week, so I'm going to just go over it briefly and just let you know that it does apply to web applications and widgets just as well as it applies to Ruby on Rails applications or a Cocoa Touch or Cocoa application.
So in this case, we're going to have our model, and it might be something like, I don't know, an RSS feed or a Rails server, something that's providing the data that you want to show to your widget or web app users. On the other side, you're going to have your view. This is how you present your data in HTML and CSS, usually.
In between them, we have this controller code. This is the code that kind of marshals the data between the view and any changes from the view back into the data. Now, if this were your iPhone web application, you would have, you know, your web app interface in the front here, but you could very easily just swap that in with a widget interface, assuming you've structured your code with MVC in mind. And then you would just provide some different controller code to make sure that the information fits into your widget instead of your iPhone web application. So now that I've talked about MVC, I want to spend some time talking about widget design.
So web applications tend to organically grow down. This is the idea of document flow positioning, where the layout of the web application just kind of flows to its natural size. So it just kind of grows organically down and stops when it's done. But widgets are a little bit different.
Widgets are like applications in the sense they fit in a fixed place, and that if there's any overflow information, you would use something like a scroll bar, okay? But one thing which I kind of want to talk about is, one of the things I want to implore you to do is to think about widget design and kind of, instead of providing, you know, scrolling content, to try to put all of your information in one widget, in one place without any type of scrolling. The real kind of essence of widget design is to get your information at a glance, so that you just look at it and then you can get rid of it again, okay? So that's something I want to implore you to think about when you're designing your widgets.
Another thing I want you to think about is taking a look at this widget, you'll see that there's this kind of pop-up menu at the bottom of the widget that lets you choose where the city that this content is coming from, where it should go. Instead of putting it on the front of your widget, where people aren't going to really touch it very often, you should probably put it on the back of your widget.
Widgets have these backs, and they're intended for things like preferences and also things like about information, so your website name and any other copyright information. That tends to go on a widget back. It's not necessary for daily use, so just put it there, let people get to it with an info button on the front.
And that frees up that whole swath of space on the front, makes your widget a little bit smaller, which is good, because the smaller your widget, that means more widgets people can have on people's Dashboard, and we want to spread the love there. And the other thing is that by getting rid of the Super Concerts banner at the top, we can put this kind of day controller at the top instead. And that actually provides something more useful in the same space.
So another thing I wanted to talk about was fonts. So you might have an amazing number of fonts on your Mac, but that set of fonts might not be on every Mac that's out there. The fonts listed here are kind of the safe fonts. They're included with every Mac.
And you don't have to sit here and write this all down. To remember what this list of fonts is, actually just pop up the Stickies widget and take a look at the fonts on the back of the Stickies widget. It's the same list. So if you stick to these fonts, you know you'll be safe and your widget will work fine on any Mac.
So another thing that'll make your design a little bit easier is Dashcode's parts library. And you saw this briefly in my demo, but Dashcode provides a number of prepackaged parts so you don't have to write all the code to get them working and you don't have to do any of the design work if you don't want to.
So poke through these things. And each one of these parts has an API to them, which is what I want to spend some time talking about, the idea of APIs. So widgets are web applications. They have a lot of web technology in them, but they're a little bit more in the sense that they're also desktop applications.
So the Dashboard provides a number of APIs for you to use for application-like behavior. So you'll see in a lot of the code that there's this widget object. And the widget object is the bridge between Dashboard and the desktop. So the widget object has a lot of APIs on it, which I'm going to discuss now.
Usually we wrap the calls to the widget object in this window.widget check. So that way if you were to take this file and run it in Safari or a regular web browser, it would notice that the widget object isn't available and then the code in between just wouldn't run and it wouldn't accept. And that's important.
You don't want to have your code accepting. So the first one I wanted to talk about was widget.openurl. So when you have an href, a link inside of your widget, if you were to just click on it, you would see that the content of your widget would be replaced with that web page. And that's actually behavior you don't want. You want it to always be present. Instead, you want the link to open in the person's default browser. So if you're using Safari or Firefox, so widget.openurl will take whatever URL is passed in and open it in the user's browser.
The next one I want to talk about is preference for key and set preference for key. This is a way for you to persist information that you want to have between restarts and logins and logouts and all that sort of stuff. So if there's any information you want to keep around, any preference that a user set, you would use preference for key to retrieve that and set preference for key to set that preference. Now, one thing to keep in mind is that Dashboard allows multiple instances of widgets.
So you can have n number of copies of your widget running at any time. When you save a preference with just a generic key, that's going to be a global preference available to all your widgets. And that might be useful, but in reality, you probably want per-instance preferences. And that's where the idea of widget.identifier comes into play. Dashboard provides a unique identifier for every instance of a widget. And so if you take this identifier and mangle it with the key, you'll get pretty much for free per-instance preferences.
So the next thing I wanted to talk about were a number of handlers that you'll want to implement in order to adopt more proper behavior on the Dashboard. The first is Widget on Hide. So if you provide a handler for the on-hide call, this is going to be called when Dashboard is hidden from the user's desktop. The reason you want to provide a handler here is to turn off any timers or any other CPU-intensive tasks. When the Dashboard is hidden, your widget shouldn't be taking up any CPU.
So you want to turn off anything that's CPU-intensive because if, you know, basically you're grabbing globs of CPU time, people are going to probably figure out it's you and then they're going to get mad at you and you don't want that. So now converse to on-hide, you're going to have on-show. So this is called when your widget shows. And this is where you can then start up time, refresh your state, read your preferences. It's a time for you to kind of catch up on lost time.
Something new we added in Mac OS X 5 is the widget on sync call. So the dock and dashboard allow its preferences to be synced across multiple computers using .mac or me.com or whatever it's called these days. And when that happens, when that sync is complete, your widget will get this call, and that's kind of a prompt for you to read your preferences, see if they're different, and if so, apply that to your widget.
And the final kind of piece of this puzzle is widget.onremove. So when you save preferences, they're just left on the file system in a file. If someone's removing your widget, you'll want to remove those by passing the null argument to preference for key, set preference for key. And that was just going to wipe out that preference. So that's something you'll want to do as well.
Now, in the kind of WebKit realm, there is also drag-and-drop functionality. People can drag-and-drop files on your widget. And that's a great way for people to interact with your widget. And this is something that WebKit provides on any DOM element. This is not unique to Dashboard. And you just provide onDrop and a number of associated handlers, and all those events will just get handled for you. Similarly, there's also support for copy and paste. So if you look at the calculator widget, there's no explicit place for you to go to select the value on the display and copy and paste it.
Yet somehow, if you're using it, you would expect to be able to copy a value in or to be able to copy a value out. So you would provide on the body element of your widget, on paste or on copy handlers, and that would be your opportunity to put things to and from the pasteboard.
and finally, there's the Widget.System call. Widget.System is unique to Dashboard, and it allows you to run any command line utility. There's a whole bunch of documentation in this. I won't get into it all, but it's a handy little thing to have. So, in addition to your HTML, CSS, and JavaScript, widgets need a couple of other things, and Dashcode provides easy interfaces to do all this work. The first is in the Widget Attributes Inspector. This is kind of a friendly UI on the Widgets Info.plist, and this is where you provide metadata for your widget for Dashboard.
You're going to be able to use your widget. So, things like identifiers and version numbers, and also stuff like localization information. Next on the list is the Default Image. The Default Image is where you basically get to preview what your widget will look like when it loads. So, when Dashboard loads your widget, while it's actually loading the widget, HTML and CSS, it's actually going to put up this default image, and this is where you get to take a look at that.
And finally, there's the Widget Icon. This is an icon which shows up in the widget bar in Dashboard. It just represents that you want to provide a unique one for your widget. So, at this point, I'd like to bring my colleague, Chris Ryan, on the stage. He's going to demo taking a widget, I'm sorry, a web app into Dashboard. Thank you. Thank you.
Thank you, Christian. I'm going to show you guys the 45-minute widget. Just kidding. So we're going to start with a web application I've created. It shows three times for different Mars spacecrafts, and I'm using very sophisticated math here, so please don't take these as accurate times. And we're going to make a widget out of this.
Before I get started, I just want to show you we have our Martian clock code here, which I've taken care to make sure that it follows the model view controller model as Christian spoke about. And so this martianclock.js file has our model and controller code. And I'm going to just put that away for now. And we're going to start with a new Dashcode project. Make a Dashboard widget.
To start, we're going to take the web application we had. It showed three clocks, and we're going to bring it down to just show one clock at a time, and then add preferences so that we can let the users choose which clock they want to see. And since Dashboard widgets allow for multiple instances, your users can actually open up multiple copies of that widget and show each item one at a time.
So we're going to shrink this guy down. by changing the width and height of our widget, and then give it a better look to make it look more like a Mars... Mars widget here. Got an image here. Add some of that nice glass that Christian was speaking about.
Now we want to grab our title. And we're going to use this-- this widget's going to be dynamic. It's going to change which clock it shows, depending on the preferences. So we want to make this title nice and big. First, we're going to make it big enough for you guys to actually see. And we're going to use Spirit for now. And make it nice and big to fit Spirit, Opportunity, and Phoenix that we're going to show here. And we want to center it.
The next thing we're going to do is we're actually going to take this widget and we're going to bring some advances. Use the same model and controller code that we had in our web application and add some new little touches. So what I want to do is actually create an analog clock as well as a digital clock, and then create some preferences to allow your users to change which clock they want to see.
To do that, we're going to use a new part in Dashcode 2.0 called the Stack Layout. It's available for both web apps and widgets. And the Stack Layout allows you to switch dynamically between different HTML snippets at runtime. And it also makes it easy to design multiple views as part of your widget. Here, the Stack Layout has two views.
We're going to make the digital view first. and the analog view and name them so we can later on switch between the views we want to see. Let's get started just doing the digital clock. So I'm going to start by dragging in the text part. Let's make that big enough so you guys can actually see back there. 40.
I'm going to build the colon in just to make it a little easier. Duplicate that. And notice once I duplicate it, I've moved the second piece over. I'm going to make my seconds. And when I duplicate it-- oh, that actually did not work. Typically, the duplication actually goes right where you put it the last time to make it easier for making multiple duplications. I'm just going to center this, remove the colon from the seconds field, and right align my hours. The next thing we want to do is actually-- get rid of that dialogue there-- is to make this white.
And now we need to actually change the IDs for these elements so we can access them later on in code. So we're going to have ours. Oops. Editing in the wrong place here. Ours. The demo gods are with me-- are not with me today. Hours, minutes, and seconds.
And last, we need to take the code that we had before, the model controller code, bring it into this project, and hook it up. So let's open that project. I'm just going to drag the martianclock.js file right into our widget. Let's take a look real quick. And here we have all the code we had before. And we're then going to actually make sure that we include this JavaScript file as part of our main HTML file so it loads at run.
So I don't want you guys to watch me type all day long. I have some code in my code snippet library to actually hook up some of these pieces. We have update time. And update time just gets the spirit time from our model code. And then calls updateDigitalClock, which is some controller code that was in the martianclock.js file, which takes the time object, and then references to the hours, minutes, and seconds elements and updates the time.
And lastly, we need to actually call the code. We have a set clock timer here as part of the martianclock.js file, which will call our code every second or so to update our clocks. So let's call that. We need to pass through the function it needs to call. Update time. Okay, so if all works well, we should have a nice widget. Here we go, we have a widget with our Mars time for Spirit in only a couple minutes.
Now, I'm not just satisfied with that. I told you we'd want to add an analog clock and preferences and those sort of things. And I want to spend some time showing you guys how to customize your widget and do things that are very widget-specific. So let's start with our analog clock. And I'm going to bring up the library. And to do that, we have a gauge part here that looks very much like a clock. And we're going to use that gauge part.
And we're actually going to stack three of them on top of each other, one for minutes, or hours, minutes, and seconds. And I'm going to go into my inspector. And just change some of the ranges. We're going to go from 0 to 12. And change the threshold. The threshold allows you to have different images based on the values.
Let's change our angles from 180 to 540 here. And the next thing we're going to do is for the front-most clock part, we're going to have a clear image so that the different parts behind it show. So I'm going to just drag that in. And rather than spend a lot of time messing with these images here, I actually have a project that already has this done, so you guys don't have to watch me do this. So just open that right up. Great. So here's our clock. So the next thing we want to do is actually hook this clock up. So to do that, I'm going to replace my update time function, pre-written code, back to my code library.
And what this function is doing, again, it's getting the spirit time. It's getting the stack layout object using the dot object notation on the stack layout element. And then it's calling the API getCurrentView, which returns the element for the stack layout that's currently visible, and it gets the ID. And if it's digital, it calls our old code before, updateDigitalClock. And if it's analog, it calls another function, updateAnalogClock, to update the analog clock.
So the next thing we need to do is actually make it so you can actually change between the different clocks. So let's add some preferences to our widgets. So to do that, I am going to add a pop-up, text part. This is going to be the Mars spacecraft. Let's make our pop-up bigger.
and to make sure that it's white so you can actually see it on the back here. OK. The next thing we do is actually fill in the pop-up object. We need to give it a proper ID. So it's going to be the craft pop-up. Before I do that, I'm going to duplicate this and use it for our clock type, the analog and digital. We're going to go back, and we're going to have the craft pop up. And then we're going to have spirit, opportunity, and the Phoenix. Hope that can spell here.
And the next thing we're going to do is we're going to actually add a handler for our Mars spacecraft. And it's going to be the Change Spacecraft Handler. And this is going to be called every time your user changes the pop-up menu. So here it's added code for us automatically. I'm just going to replace that with some code I've already written.
What this code is doing is it's getting the title element that we created before. This is the title on the front. it is getting the value of the popup elements, setting the title to that value, so Spirit, opportunity, or Phoenix, and then calling widget set preference as Christian spoke of, passing in the craft name, so Spirit or opportunity, and then calling a built-in dash code convenience that basically takes the widget identifier and the key you pass in to create a per instance specific key. So let me do that real quick for the clock type.
[Transcript missing]
Zedor there. And we will make a handler for when you change that type as well. Change clock type. And again, I have handy code here to replace that. So again, we're grabbing the value of the type pop up so we know whether it's digital or analog. We're also grabbing the stack layout element and grabbing the object from that element to access the JavaScript-backed object for the stack layout.
And we're setting the current view of the stack layout to switch between the analog and digital views at runtime here. And lastly, we're setting the preferences so this can be saved later on. And the next time the widget is opened, we can actually retrieve these values, again, using the create instance preference key convenience function. So now we've saved these values. We've learned how to switch back and forth. We need to actually load these values the next time the widget is loaded or when you hide or show the widget. So to do that, I have a load preferences function.
And the load preferences function does the similar things. It calls widget preference for key, and it calls create instance preference for key with craft and type to get the instance preference for these two items, and then sets the pop-ups appropriately, and calls change spacecraft and change clock type so that the views are actually updated.
The next thing we need to do is find a location to call these preferences or to load the preferences. And we have to go to our load handler. When your widget is loaded, the load handler is called. So we're going to load the preferences. And also, as Christian spoke of, when your widget is synced with .Mac and the preferences are synced, we need to update our preferences.
So before I go on, there's a couple things that I want to speak of here. One of them is that our title may update in the front, but we're not actually updating which clock that we're getting the data from. So I'm going to make a quick change here.
And any of you JavaScript people might want to close your eyes for this one. It's a little bit of a hack. Here I'm grabbing the craft pop-up and then evaling the get spirit time function or whatever the craft is at runtime. We can close our eyes for that one.
One of the things I want to speak of is the code is already here, but make sure you guys understand this. When you hide the function, when the widget is hidden, you want to stop your timers so that you're not actually processing data in the background. And then again, when the widget is shown, you want to actually start your timer back up again. And one last thing with preferences.
is when your widget is removed, you want to make sure that you actually remove the preferences so that if another widget of a similar identifier comes up, you don't get your preferences in there. So we have craft. And we have type, so we're going to move those preferences. So let's go run this real quick and see if it works.
Hopefully it does. So here we go. We have the spirit clock you saw this before, the digital clock. We can flip to the back, get our preferences, change our clock and the type. And here we have an analog clock for opportunity. That's pretty good. So now we want to do our finishing touches on our widget.
I want to go to the widget attributes, make sure that we have an appropriate identifier to identify our widget. And I like this here, my Martian widget step one. Actually, let's just make it Martian widget. We want to go to the default image. And the default image is what's shown when your widget first launches before the preferences are loaded and everything else is running.
And we don't really want the digital clock here because you may have your preference set to show the analog clock. And so to change that, you can go to any element in your widget and in the inspector, You can change the-- it's kind of hard to see here, but the show in default image setting. And I'm going to go to the stack layout and just make sure none of the clocks are showing. So I'm going to remove that from the default image. Let's go back there. So there we go. It's not showing.
Go to the widget icon. That looks pretty good. The icon, you'll see in a second, it shows when your widget is first launched into Dashboard. And now we're going to go to a new panel in Dashcode 2.0 called Share. And we're going to change some of the attributes here that our widget has, and we're going to just-- Give it our name, Martian Clock. And all the rest of the settings look good, so we're going to deploy to Dashboard. And here we have that icon. It's asking us if we want to install on the Dashboard.
And there it is. Let's keep it. And so here we have our widget, and the great thing is, we can open up multiple instances. Let's do three, one for Spirit, Opportunity, and Phoenix. Change the clock type and the actual Mars spacecraft it's showing. And there we go. Just in a couple minutes, we've taken our web application and created a great widget to deploy on millions of Mac machines out there. Thank you. All righty. Back to slides, please.
All right, so what Chris just showed you is how if you structure your code using Model View Controller, he was able to take a whole swath of code that does all the calculations for the clocks and just drag those right into his widget. So by just being smart and kind of laying out the code ahead of time, he's saved himself a lot of hassle.
Beyond that, he's made a unique design for his widget. His web application had all three clocks at once, but on Dashboard, he let it be so that every user can have whichever combination and whichever style of clock he wanted. So that's a very handy, good point to take away from there. He also managed to leverage Dashcode parts to save himself a lot of hassle. By using the gauge part, he was able to make a clock very trivially.
He used widget preferences to make sure that if a person logs out, when they come back, each clock will remember which type of clock it is and which clock it's showing. And finally, he provided unique information for his Info.plist, a unique default image, and a good widget icon so that his widget will stand out amongst all the others. So now that we've spent time talking about taking your iPhone web application and making it into a widget, I'd like to spend some time talking about making widgets into web applications.
And I'm just going to sound like a broken record now, but Model-View-Controller really is the one paradigm you want to use in this whole thing. Doing this will just let you move a big swath of code between one and the other, and it'll make your life a lot easier.
Beyond that, I want to talk a little bit about layout. So this here, let's pretend this is kind of something that's a widget that we've just brought right over onto the phone. And it's positioned using absolute positioning, which is typical for just about most widgets. What happens in absolute positioning is that an item's placement is set hard-coded absolutely versus the top left corner of its view. So you'll see the box with the text in it right now is 35 pixels down from the top. And when you have just two lines of text in there, this is fine. It fits in the box and everything works great.
And if this were a widget and you wound up having more text, you would just put a scroll area, you'd wrap it in a scroll area and get a scroll bar on the side. But the thing is, on a phone, that would be kind of strange to have a scroll area inside of something that already scrolls with your finger.
And so instead of putting a scroll area in there, you would want to make sure that this box doesn't have a fixed height of 50 pixels, but instead sets its height automatically, which means it would then just be kind of growing with the size of its content. But now you'll see the problem, and that is that this box that was underneath the one that... that has the text in it has now just fixed positioning and they're colliding and it doesn't look very nice.
So instead of positioning it absolutely versus the top left corner of the view, I want it to move its... I want it to position itself relatively using document flow positioning and have it be 10 pixels away from the one before it. So that means that no matter what the size of the element above it, this one will always be after it.
So this is Dashcode's document flow positioning. This is the metrics inspector inside of Dashcode. And I lose it. So this is Dashcode's document flow positioning. This is the metrics inspector inside of Dashcode. I alluded to this in my first demo, but you'll see that there's this new pop-up for layout up here.
It lets you choose between absolute and document flow positioning. And then once you've decided to go with document flow, you'll see that you can set the margin, where this element sits with respect to its neighbors. I, in the previous slide, had an offset of 10 pixels on the top, so I would set that top value to be 10. Also, you can choose your auto-resize behavior. If you're familiar with Interface Builder, this is a springs and struts kind of idea. And then finally, you can also... You can also set some constraints so that each element has a minimum height or maximum height.
So also, when you're bringing your widget onto the phone, Dashcode's part library, again, will prove to be very useful. We have a number of parts for things like arrowed buttons and call and map buttons. If you just bring these in, you won't have to do any work. You can just provide whatever URL you want to go to, and these all just work for you.
Also something we have in Dashcode, too, is an improved glass styling. So you saw that in the first demo. Play with that. It gets really good results. We also added this new styling called Recessed, and it makes the plastic look like it's pushed in. So this is something we think you'll like, too. So go ahead and play with that as well. So now to show you a little bit more about bringing your widget as a web application onto the iPhone, I'd like to bring up Han-Ming Ong. Han- Thank you, Christian.
So in this part of the demo here, we're going to take a widget and do two things to it. First thing we're going to do is to-- well, we're going to convert the widget into a web application in a sort of a quick and dirty way so they get a feel of it.
Next thing we're going to do is to apply some design touches to it so it looks and feels like a web application. All right. Now, the widget we're going to use is a widget that most of you should be familiar with. It's a tile game widget in Dashboard. For those who have not seen it, I happen to have a copy here.
And I'll just run it for you guys who have not seen it before. So when you run it, the tower board with 16 tiles, one empty, would come up. The first click shuffles it around randomly. Second click stops it, and your objective is to start clicking around the tile to rearrange it back to the original configuration.
Let's take a look at the JavaScript file, actually. Now, the original author has done a very good job in adhering to the Model-View-Controller pattern. For example, this is his TAL class right here in a generic class way, a generic class description. And here, for example, is his controller code set on board that interacts with his Model class.
And another example is, it is a layout function which interacts with the view. All right, we're going to reuse this JavaScript file wholesale because everything there is, you know, good for us. I'm going to create a web application now. And for this demo, we can actually start with the custom or the utility. We're going to make use of the utility because we're going to build on top of it. So let's do that.
Now, very, very briefly, the utility template, this project here, has two views, the front and the back. The front has a message element that allows you to type in your message. The back has some settings, like font and so on, that allows you to customize the message. It makes use of a local database to save the settings.
Now, we're not going to create a message element, so we're going to delete it. And the next thing I'm going to do is to drag over my tilegame.js into my new project. And let's make sure that our HTML is aware of it. So let's go over there. and include tallyan.js right here. Let's just save it. All right, so I'm also now ready to copy over the CSS selectors. And since there are only three of them, I'm just going to copy and paste to my main.css.
The widget actually has image resources that I want to reuse as well. Just drag over and drop it over in my images folder. I'm not actually ready to copy over the DOM element, but I want to box it up so that I can move them around later on if I want to.
So let's just call the box "towels." and resize a little. And I actually don't want to have a stroke, so let's get rid of the border. Now, let's go over to my widget. HTML, and there are only three elements I want to copy over. So let's just copy. and go back to my index.main.html file. Find our box. And this is where I want to drop it in.
So let's make it look nice here. Oops. If I do this right, if I click on the canvas right now, they should just appear here in my DOM. And let's center it a little. Last thing I want to do is to make sure that the initializer codes that I have in my tilegame.js has a chance to be run. So here is my three initializers. I'm going to cut and put it in my real load function, which is in my main.js.
and delete stuff that I don't need. This is the message element. I want you guys to just ignore the database part for now. We're going to touch on it a little. And let's just run it. So the first quick and dirty port actually brings it over. You actually have your tile game right now as a web application. The animation is still there. You can go ahead and play it.
So next thing I want to do actually is to style it a little so that it looks and, you know, feels like a web application. Let's change the name to TileGameForce. And if you notice, when the widget comes up, in this case, the game comes up, it has the yellowish tinge to it. So let's just make the color harmonize. I happen to have three chips here, color chips. Let's not forget about the border.
And then when the widget comes out in Dashboard, it has a board to define its boundary, because in Dashboard, there are other kind of widgets running. But on the phone, the screen defines the boundary. And actually, here, we actually have the background image as the boundary. So what that means is I actually don't need the board. So let's just delete it. And now, but the white is gone now. So I need to put it back. But I have a box to do that. So let's just do that. And maybe a little bit of transparency.
Next thing I want to talk about is pretty important. Now, on the widget, we define a 36 by 36 pixels tile. That's good enough for the desktop because the mouse is a very precise pointer. You can just accurately hit on the tile. But on the iPhone, your finger is now the pointer, so you want to make the tile pretty big to make it easier for the finger to select the right tile. So I'm going to make it 60 by 60. So let's just deal with a great four-start. 60 times 4 is 240, okay? And make the box size big as well.
And just do some alignment here. Let's center it again. So, okay, we have done the change to the view. Let's make sure our code agrees. And again, the author has done a very, very good job in making sure that we have a variable here that defines the size of the tile. He uses 60, so I'm going to change it to 60.
And the rest of the calculation, you can see, is based on this variable. So by just changing one variable, I get the whole effect. So let's run it again. And this time it actually comes up, and it looks like a web application, much bigger, way easier for your finger, and you can still see the animation going on here. So back to you, Christian.
All right, thank you, Han-Ming. So Han-Ming has shown you how you can take a widget and very easily bring it into the iPhone as a web application. But I'd like to spend some time for you widget developers talking about moving some other things that you're probably used to doing from widgets to ways that you can do them in web applications that work with some new features on the iPhone and iPhone OS 2.0. The first one of these is WebKit transitions. So if you're a widget developer, you're probably familiar with the Apple Animator JavaScript class, and it's a timer-based implementation used for animation.
And something new in iPhone OS 2, though, is this declarative CSS style of animation. And basically what you're seeing here are three properties on an element. And one just tells you which properties should be animated. Second one tells you over what duration they should be animated. And finally, the third one tells you which timing function to use. And ease in/out in this case kind of gives you a nice curve of animation. So you can do this all declarative in CSS. And on top of that, you get the added benefit of it being hardware accelerated.
So we definitely encourage you when you're bringing your widget over onto the phone to use these CSS values. There's a session that you'll be able to get on iTunes a little bit later devoted to this whole section. It was actually passed Wednesday, and it's all about enhancing web applications with transforms and animations.
In addition to that, you can just use Dashcode's stack layout part. Chris showed this in his demo, but it's also available for the iPhone web applications. And when you use one of these stack layouts, you can set transitions between the different views. You'll see in the inspector, you get this little pop-up that lets you choose between pushes, slides, and also 3D effects like the cube and the flip animation.
So you can get all these things for no code. You won't have to write any code to make them work. So give them a shot, play around with them. And one thing to remember about the stack layout is that it works for large layouts, or it also works on a per-element item. So you can use them as large or small as you like.
Another feature that Han-Ming alluded to was the database feature, the client-side database feature. So if you're used to using preference for key and set preference for key in your widget, you'll know that those aren't available for iPhone web applications. Now, we do provide in iPhone OS 2.0, though, this database functionality, and this allows you to store information using SQL statements. To open a database, you would call Open Database and provide a name and a version, and once you do that, you would set up a new transaction.
You'd pass in a closure to that. That gets the transaction back, and in the transaction, you would just execute an SQL statement. So this is a great way to save preferences, any type of state, any information that you'd like to be kept around. Now, there are some caveats that you should be aware of, too, when you're bringing your widget over to a web application. First off, if you're used to using Widget.System, being able to call any command line utility from within your widget, you won't be able to do that inside of an iPhone web application.
Also, Dashboard uses an Elproj-based system for localization. That won't work on the iPhone, either. But you should be able to bring those strings just into your JavaScript file, and that should work fine. And finally, there's the single source restriction. Now, most of you with widgets are just used to being able to take an RSS feed or a feed from anywhere and just bring that into your widget and not really have to worry about this limitation.
But if you're running a web page inside of any browser out there, you need to make sure that the feed is originating from the same domain as you're being hosted. So, if you're using a web page, you need to make sure that the feed is originating from the same domain as you're being hosted. If not, you will get an error, and it won't load that feed. There are a number of workarounds available using Apache stuff and PHP scripts. I'll let you Google for those, but that's just something you should be aware of.
So, I also want to talk about a session that we did this past Wednesday afternoon, and it was devoted to using Dashcode for making iPhone web applications. I won't be able to go into nearly all the stuff that you can do with Dashcode for iPhone web applications, so take a look on iTunes after the conference and take a look at this session. And with that, I'd like to bring... I'd like to bring my colleague Han Ming-Ong back on stage to finish off the TileGame web application.
I didn't know I'm called a Pazier Master here. So second part of the demo, we're going to take the web app that we have developed so far, and we're going to do three more things to it, all of which are more code intensive. First thing we're going to do is to migrate from the JavaScript-based animation to the new WebKit transition-based animation. And then we're going to allow a way for the user to select and choose a new picture. And finally, we're going to save that new selection to the database. All right. Demo machine, please.
Thank you. So this is the web application where we last left it at. Let's save it. Okay, so first thing. First thing we're gonna do is migrate to the WebKit transition. And to do that, we need to let the transition engine know to register our intent to use it. So I'm gonna find the function here. This is a function that tries to find the bot element for each tile, the element. I'm going to drag in a snippet from my library.
So what it does here is pretty simple. Once you get hold of the element, it gets its inline style. And it's telling the engine that, look, when the top or the left changes, I want you to do some animation over it. Now, as Christian mentioned, there's actually a session that goes very deep into it. So here, the demo time is short, so I'm just going to talk about it.
And then the next thing I'm going to do is to change one of the functions that actually does the animation. Here, I'm going to delete the old stuff. And this whole chunk right here, actually, I'm going to reuse because what it's doing is calculating the top and left. So I'm not going to throw away good code that does all this good job. So I'm just going to reuse it. But here, let's delete away the old code.
and drag in another piece here. So once I have the top and left, again, I stick it to the style, and when there's a delta, I can trust the transition engine to do the animation for me. Next chunk over here, what it does is instead of making use of the old JavaScript timer base to call you back, timer to call you back, it makes use of a more DOM-friendly way. So I'm registering for a new event called -- well, surprise, surprise -- WebKit transition end event.
And when the event ends, my callback will get called, and I get to decide if I should keep the animation going or not. There's a whole slew of changes I need to make to this file, and you are not here to watch me do repetitive stuff, so I'm going to jump to my first snapshot.
All right. So this is with all the changes applied. If I run it now-- You will see not much of a change. The animation is still like before, which is good. But the point is, behind the scene, I just got rid of about 100 lines of old code, which you don't have to maintain anymore.
I put in probably 10 lines of very clear CSS declarative way of saying I want the transition to take over. So for that, it's very easy to read, very easy to maintain in future. And also, really, you're making use of the engine now, natively on the phone, to do your animation. So when the engine picks up a Revit's performance, get more frame rate in there to have a smoother animation, you just benefit from it for free.
All right. Now that we have done the migration to the new WebKit transition, I want to talk about a different topic. On the widget, there's a way. I don't know whether it's discoverable or not, but whatever it is, you can drag a picture from Finder or a different application, hit F12, and drop it onto the tile game board, and you have a new picture. But obviously, there's no drag-and-drop between applications on the iPhone, so we have to have a new way.
So we sat down, brainstormed a little, and realized, okay, why don't we make use of the back to let the user select the pictures? Now, there's more possibilities here once you do that, because the web app is actually hosted on a website. Webmaster could have a different page for PC or Mac users for the other users to load the pictures of their kids, their grandkids, their pets. And when it's served out to the iPhone, you can have a different set, a random set. Here, we're just going to have six pictures of the cats. And we're going to represent our operating system, starting from Sheeda to a leopard. So let's do that.
I'm going to go to the back and delete away the stuff that I don't need. I'm going to keep the purple color chip just to have an easier time to change it. So again, I'm going to make it easier for my finger. For my user's finger. And instead of the gradient, I'm going to have an image fill. I happen to have six images here.
Let's drag it over to my images folder. And I'm going to look for Leopard. and drag it over to my image well. There you go. Let's move the check mark over here as well. The check mark is simply something that we put in there to denote that you have chosen this picture.
So next thing I want to do is, what happens when the picture is clicked? So you want something to happen. So here, because we make use of the previous color chipped, it says color changed. I want it to be picture changed. All right. I got to do this five more times because I have five other pictures. Again, let's not do that on stage. Let's open up my second snapshot. Excuse me.
So now I have my six pictures. Now that I've allowed a user to choose the picture, I actually need a function to vent it out. So let's do that. Get picture name. It makes use of a global variable picture name, and it just vents it out whenever it changes.
I now want to write my picture change function. Instead of writing it from me, I actually can reuse this function right here. Color changed. And instead of looking for the substring color chip, I want to look for picture. Once I get it, I want to have a new function update picture to do my job. Again, let's ignore the database part for now.
is dragging my update picture function. What it does is pretty simple. It makes sure that the check mark appears underneath the right picture. updates my global variable picture name and calls a relocate image function in my tilegame.js, whose job is simply to point to the right image now. One more thing I need to do, which is to make use of this get picture name. Now, in my tallygame.js, in my layout function, we actually had coded. This is from before, right? So let's just make sure it makes use of our new function.
All right. Let's run it. Flip to the back, and here you see our 60 majors. And I want to start with my cheetah picture. So, and I start playing it, and then suddenly I have a Napoleon Dynamite moment. I'm like, oh, do cheetahs have retractable claws? And, you know, I'm really obsessed with this question now. So I want to go and find out.
Do chickens have talons? Anyway. So here's my Google. I go and find out. And now I found the answer. I want to go back to my game. Gosh! Now where's my, where's my, you know, cheetah picture? Now I have my leopard picture back. Well, it's simply because we didn't save it, right? So we have a new way to save it. And as Christian said, in HTML5 on the iPhone, we actually have the local database.
I'm going to make use of it to save it. Let's quit. Let's go back to main.js. Our database is-- for this simple case, we're going to have a table. And this table is going to have a simple key column and a value column. And we're just going to save the last selected picture.
You can imagine you want to save the position of the tiles and so on. We'll leave it as an exercise for you guys. So let's uncomment the unitdb. My database is going to be called Talgame. This is the user readable string. If the table doesn't exist, I need to create it. So let's call it initTileGame.
Let's change it there. Delete stuff. Like I said, I'm going to only insert a row. So, The key is going to be picture, and the original value, I want it to be just leopard. Go back to the initdb function and change a couple more things. Now I want it to be called load-- once I've initialized the table, I want to load data from it.
So let's look for our load message. All right, so once you get the results set, this is a SQLite table, so if you are familiar with it, you can see it'll be like a fish in water here. So once you get it, once you find the key, which is picture, which is what I just put in, you want to update, make use of the function that we just wrote, which is update picture.
Oops, sorry. All right, right, by now you're pretty sick of my typing, so again, I'm going to make use of similar changes. So let's not do that on stage. I'm going to open my last snapshot here. With all the changes applied, you can see that. So let's run it one last time. Maybe not.
So again, you have Leopard. I'm going to flip to the back. And now I finished my Cheetah. I want to start with-- I want to play the Puma game now. And you go ahead and click on it. And obviously, you have another question, right? So what's the difference between Puma and Mountain Lion? So what's-- I'm going to find out now. When you go and do your Google search and happy with the answer, you go back, and when you go back, there you go.
That's your Puma. Actually, one last thing, because I think this is pretty-- so you spend a lot of effort and probably money also in paying your web designer or someone to come up with a nice icon, right? I mean I actually think this icon looks pretty nice. So I want to reuse it in my web application. So to do that, what you do is you go to your home screen icon composer, drag in your icon.png from the file view of your widget, drop it over here in the white area.
And we make sure that we down sample it to this interesting 57 by 57 size that the iPhone takes so that it looks nice. So in this simple step, you have a pretty nice icon here. To prove it to you, I'll just run it. And you have a pre-- you know, you like this game now. So you're like, OK, let me put it to the home screen.
And-- Let's call it tile game. So whenever you're sick of the job that your boss gave you, you're like, OK, let me play the game, man. There you go. All right, back to you, Christian. Thank you very much, Han-Ming. So to wrap up this portion here, again, Model-View-Controller really kind of made bringing the tile game over into the web application very easy. Beyond that, Han-Ming was able to use Dashcode Parts to kind of jumpstart the development.
WebKit transition is a great way to kind of replace JavaScript timer-based code and use hardware-- get hardware-accelerated transitions and animations for free. And finally, he was able to use the client-side database stuff that's in iPhone OS 2 to replace his preference code. So to conclude our session today, I just want to encourage you that if you have an iPhone web application today, to make a widget.
And the idea is that you would be able to get onto every Mac user's desktop and have a presence there in addition to your iPhone users for a small amount of work. So we think this is gonna be something that's gonna be real easy to do and that we'd really like you to do. And also, if you have a widget, why don't you make a web application as well? Take that code that you already have, reuse it, and get it to a whole new platform that's the hottest thing going right now.
Model View Controller will help you get there. So we really want you to use that and adopt that paradigm. And Dashcode is the IDE that we think you'll want to use to make that all happen. So for more information, we have a whole slew of evangelists who are ready to answer your questions.
Matt Drance is the Dashboard evangelist, Mike Jurowicz is the Tools evangelist, and Vicki Murley is the Safari Technologies evangelist. So send them your questions. We have a couple of sessions that have happened. You'll be able to pick these up in iTunes, but we have the Dashcode session for iPhone web apps and three iPhone-related Safari sessions for you.