Essentials • 56:10
Offline data capabilities in Safari open up new possibilities for web application design and data management. By combining SQL with standard HTML and JavaScript, novice and advanced programmers alike can store data persistently between sessions, save data locally before submitting it to a remote host, and enhance the overall user experience and functionality of their web applications.
Speakers: Brady Eidson, Ada Chan
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 afternoon, everyone. Got the imp
- Thank you. Welcome to session 411. We got the information for you today, utilizing offline data in Safari. My name is Brady Eiadson. I'm a software engineer on the Safari and WebKit team at Apple. How many of you out there are web developers, develop web applications and widgets? Most of you, I would expect.
How many of you are native application developers? A lot fewer. I noticed a little overlap there, but there's a disparity. And I hope to convince you today that you're not so different from your peers after all. We have these web applications online. What's been going on? The web used to be static and simple, and it's gotten a lot richer lately. We've seen a lot more creative and a lot more sophisticated applications out there.
What's been going on? We've been seeing media being used in new ways. We've been seeing Office web applications, keeping in touch, instant messaging and mail, all this great stuff. And they're doing things that only native applications used to do before. And this is just awesome. And they're looking a lot more like native applications too. Especially with the web applications we've seen pop up on iPhone that just feel, I forget I'm using Safari on the iPhone, they feel so much like an iPhone application. It's pretty awesome.
Now, what's been going on here is that we have these things I like to call rich internet application technologies. There's a lot of technologies that have been around for years on the web and a lot of new ones. Certain browsers are pushing in new different directions. Many of these things are becoming standards, and they're allowing you to make these more native applications.
I can think of just a couple I had to try really hard to fill the entire slide here. We're dealing with text. We're dealing with media images. We have great JavaScript developer tools, UI effects with the great CSS styling. And these are just a couple. There's really a lot more of these.
But I look at this list, and I ask myself, what about the data? Some of you might be asking yourselves, what data? One thing web applications do great is data can live online in the cloud, and many content providers keep large amounts of very interesting data out there online. But there's a lot of other stuff, too.
Preferences and settings, for example. Your users have a specific way they like using your application. And also application state. If there's some long-running procedure, where was the user in the middle of that so we can recover later? Or where was the window? How big was it? That type of thing.
And then finally, user data. Hopefully your users are generating some cool stuff with many of your web applications. And that data, well, it has to go somewhere. It's probably on a drive somewhere. And I hope to convince you today that that drive can be, and sometimes should be, in your users' machines.
So what are we going to talk about today? I'm going to briefly touch a little more on the state of the web, some of the tricks you guys are using to manipulate data in your web applications. Then I'm going to talk to you about some of the new solutions, starting out with the HTML5 storage interface, a great standard way to manipulate data as easily as filing it away. It comes in two concrete flavors you'll learn a little more about.
Then we'll just jump right into the power of the client-side databases. Another HTML5 standard, pretty awesome. And then finally, we'll touch on the HTML5 application cache. If you're unfamiliar with what that is, I don't want to spoil it for you now. We'll get into it at the end.
So right now on the web, we have all these great web applications that are doing amazing new things that used to be only for native applications. And they're moving all this data around. This data has to go somewhere. First, let's talk about an ideal world. How would we like our data to be when we're manipulating it? Usually we want our data to be pretty persistent, right? We want to know we can store it somewhere and it's rock solid and it'll always be there when we come back to it days, weeks, months, years later. It's going to be there. Also, manipulating data should be fast. It's unacceptable to lock up the application, for example. Users aren't standing for this anymore. Even in the web browser, things seem to stay responsive.
Should be easy, right? You shouldn't have to jump through unnecessary hoops simply to handle the data that belongs to you, your application, and your users. and simple data and complex data. We want to be able to manipulate all sorts of data. And finally, we want it to be secure.
Our data is a very sacred thing to us and our users, and hopefully we want to make sure that only your web application and your users can access it. So you've come up with a lot of great tricks to do all these things in the standards gap, as I call it, where there's no real good way to manipulate it, but you've made do. One way you've made do is by using cookies.
Cookies, which were originally designed for HTTP integration to automatically send data off to a server with a request. So what they were designed for, they're great for that, always will be. But for the other things we're looking for, Cookies are pretty fast. Most browsers load all the cookies into memory. It makes it really quick to both access and to change the cookies. They're also very secure. They have the same origin-based security policy, which is becoming the standard security model on the web.
But they're not persistent. Some of you might think, well, of course they are. I use cookies. I think they're persistent. I set an expiration date far off in the future, and that expiration date means squat. The browser is actually allowed by the cookie spec to delete these cookies at any time. And in much more limited browsers, they might not ever be persistent in any way.
And cookies aren't easy. They're key-value pairs, such simple data. But how many of you are familiar with writing a parsing function just to manipulate your cookies and then reconstructing that string just to store it away? It's such simple data, it should be easier, right? And they're not that flexible. They are simply only key-value pairs.
So another great development for manipulating this data has been the XMLHttpRequest, which was designed for syncing data to a remote server to be brought back down to a browser later, perhaps, or to be used in mashups with some of the cool things that we talked about in the Ajax talk this morning. And that's great. It's designed for that. It'll always be good at that.
For some of these other things, there's some questions. It's obviously flexible, right? We can handle any type of data, handle our own protocol of shipping it up and off to the server. Simple data, complex data, that's great. It's as persistent and secure as you make it, right? How do you transport it to the server? Do you encrypt it? Do you have armed guards in the server room handling the data to make sure no one gets to it that shouldn't? But I've never heard anyone say XMLHttpRequest is easy.
How many of you have manipulated it directly instead of using some JavaScript library? I haven't since college, and I choose not to because the JavaScript libraries make things easier, but they don't have to worry about the JavaScript backend. I'm sorry, the server backend to handle that data. And as far as being fast, networks will always be orders of magnitude slower than keeping data local to a client's machine.
And then there's plugins. Plugins which were originally developed to run custom native code in the web browser where the web standards didn't have any other solution, right? And it's what they were designed for. They will always be great at it. I'm not going to give plugins a passing grade in anything else because there's too many question marks up in the air.
If you're using a third-party plugin, maybe it works better on one of your user's platforms than another user's platform. And if there is a bug with it, how do you get the third party to fix it? And if you're writing your own plugin, you have to make sure that it works on all the same platforms in all the same ways with all the same features. And the performance has to be the same. And then some IT administrators won't let your users install plugins. And some users are too lazy to install plugins. And some devices don't support plugins. And the problem is it's just not standard web technology what plugins do.
So some of you are aware of some of these caveats. But seeing it presented all in one time in one place, it might throw you off a little. You might say, OK, I never knew all of that. I thought I was handling this data fine. But if I can't do these things, what else can I do? And the path forging HTML5 standard is trying to fill in some of these gaps. So let's talk a little bit about the storage interface.
The storage interface is meant to be able to handle simple data in a simple way, as simple as filing something away. And we're bringing this to you for the first time in the Safari 4 developer preview. The type of data that you can store is the exact same as cookies, key value pairs. As a matter of fact, they're strings only like cookies, but without some of the caveats of cookies. For example, cookies usually have about a 4K limit per cookie. There's none of that here. There's no limit on the number of items you can store.
And various ways to manipulate those items. Cookies are complex. We have to rip apart the string and then mush it back together when we're done. And there's a few ways to manipulate items in the storage interface. And in fact, if you know JavaScript, JavaScript objects and their properties, you already know how to use this. You'll see that really quick here. has same origin-based security, the same as most of the security models. So your domain and your documents are the only ones who can see your users' data.
comes in these two built-in varieties, which fill two different niches. These are both new objects on the global window object, the session storage and the local storage object. And then finally, the storage events, so you can be notified when the data stored in a storage object is changed.
So these two concrete implementations, what are these? First one is session storage. So what does session storage do? I ran into a problem a little while ago. I was taking vacation last year. I wanted to visit Seattle. I love going to Seattle. And I also wanted to go to Paris for the very first time. So I opened up a flight booking website in two different browser windows. I found the perfect flight to Seattle. I found the perfect flight to Paris. I purchased both, and the confirmation email told me I was the proud owner of two tickets to Seattle.
So what the heck happened? They were using cookies in the browser to track my session. Now, cookies are great for tracking a browser server session, but only in the browser. They come up short. There's this information leakage between windows. So session storage, it's trying to solve that. It's trying to replace cookies for session tracking.
And it accomplishes this by giving one session per browser window. So the session storage object in two different windows of your web app will start out completely independent. If you call window.open and create a new window, then it'll get a copy of the previous and it'll diverge from there. It's pretty sweet.
So in this other one, local storage, this is as simple as filing things away, but it's a little more concrete. And by concrete, I mean it's persistent. Objects stored in the local storage area will be on your user's disk inside of their web browser, and you will predictably be able to get them back at any later date. Unlike session storage, local storage is global, so all documents from your domain can access the same local storage area, no matter how many browser windows it's opened in.
So that's session storage and local storage, solving two different problems that exist out there. But how do you use it? How do you file these things away? Let's call set item on a storage object, and it's that simple. Key and value, like cookies, we don't need to deal with the strings. Just do it. And like I said, if you know JavaScript objects and their properties, you can also use square bracket property notation and dot property notation because the keys are properties on the storage object.
It's that easy. How do you get things out? You might imagine the exact same way. Since they are properties, you can iterate over all of the keys in the entire storage object. And you can remove an item as simply as calling remove item, or just wipe the entire object out in one fell swoop.
So here to give you a quick demo of a simple way to use local storage, I'd like to invite my colleague, Vicki Murley, the Safari Technologies Evangelist, on stage. Thanks, Brady. So Brady's given us a great overview of the kinds of things you can do with session storage and local storage. Can we switch to the demo machine? Okay. Other, Demo Machine A.
Well, I'll talk some more while this is getting resolved. So I wanted to make a game to demonstrate local storage. And to do so, I was thinking about you guys and how in every Q&A session I heard, I'd like the sample code to be available afterwards. So I made my tic-tac-toe game. Originally, it was just text, Xs and Os. But last night, I made it extra gratuitous. So you'll see in a moment, using a lot of the techniques that you've seen in the other sessions.
In order to present this game, I am going to need a volunteer from the audience. So anyone out there who would like to get on stage? Anyone? No one over here? Anyone? Oh, great. Gentlemen over here, young man, come on to the stage here. Okay. Brady Eidson. Give him a big hand. Okay, so here's my game.
And Brady and I are going to go head to head here and you're going to wait. She's going to go down is what she said. That's what he thinks. Okay, so I'm going to make this move right here down in the lower left corner. I'm gonna go over here.
Nice move, but I think I'm now going to go upper left. I see what you're doing. I see what you're doing. I know how to react to that. Okay. Okay. Okay. Well, you reacted well on the block, but now I'm going to take the middle square, and not an easy one to defend against. Okay. I know what to do. I know what to do. Oh, I hit Command-Q instead of clicking on my move. Oh.
I'm sorry. Oh. Well, now if I load my game again in Safari, we see it's totally gone. Are we going again? No. No, we're not going to play again. So as you can see, the browser was quit, and my data that I wanted to store didn't persist across that quit. So I'm going to modify my code so that any underhanded competitors that I might be up against next time can never defeat me in this hardcore game of tic-tac-toe.
So here is my JavaScript code. And really, to store my data, I'm just going to do three things. In my HTML, I just have a nine element array, which represents my board. And each table cell has an ID of 012 to map to that array that I'm maintaining here in the code, which is this board right here. So the first thing, usually when I update the board, I just set my board in the code here to this marker.
But now I want to also add a little bit of local storage magic. So local storage is great for storing strings. So I'm just going to parse my array really quickly to a string by iterating through the array and delimiting everything with a comma. And then the key line here is I'm setting this item tic-tac-toe board. And I'm going to add a new string to this board string that I've created. So that's step one. Now every time I update the board, I'm also storing it in my local storage database.
So step number two, usually when I create the board, I'm just going through and setting every element of the board to empty, because I'm essentially clearing the board whenever I create a new board. But now I'd like to create a board from the stored version, if I have one. So since this is really just duplicated over here now, I'm going to delete this.
That's now up in my else block. And so now I check local storage to see if this item tic-tac-toe board exists. And if it does, I'm just going to split on that comma that I used earlier, iterate through the board, and fill that square with the appropriate marker.
Otherwise, if that doesn't exist, I'm just going to clear the board like I usually would. The last thing I want to do is I'm going to do is when I have a new game, I want to delete my stored version of the board. So that is just a one line change. And that is local storage dot remove item tic-tac-toe board. So now I'm going to save this.
I'm going to reload my game and I'm going to put a couple characters here. And as you can see, I can quit the browser. And reload again, and my characters are exactly where I expected them to be, my Xs and Os. So I can also open a new window, and the data is still there. It is working exactly as I thought it would, so it's perfect. And that's it for local storage.
Thank you very much, Vicky. And local storage has been a pain for me, and it's a pain again now because now I can't cheat at tic-tac-toe anymore. and I are going to talk about the demo. So, this is a demo of the Tic-Tac-Toe game. It's a little bit unfortunate. So, that demo was pretty simple. We saw how easy it was to use the minimal interface, no complicated cookie manipulation just for these key value pairs, and we saw that it was persistent.
I also mentioned earlier the storage events. The storage events are great. We can keep documents in sync. So one solution for the flight booking problem would have been if they'd used session storage instead of cookies for the in-browser session, I wouldn't have had this information leakage. Right? And I would have gotten the two flights I expected. Alternately, if they were using local storage, for example, and it was global data, they could have used storage events to keep the documents in sync.
Now, whenever set item, remove item, or clear is called on a storage area, an event is sent out to everyone else who might be listening for it and might be interested. This is pretty cool to keep things in sync pretty easily. You can listen for the named storage event, or there is a standard on storage attribute that you need to put on the body element.
For session storage, since one page, one browser window is the session, every frame in that window can be notified. For local storage, every frame in every window can be notified. So if your users have your web application open in 80 different tabs and they're kind of insane like that, all of them can stay in sync.
It has all the goods you need to know, the storage event. It has the key, old value, new value. And then it also has the URI and the window object that generated the event. And that's it for storage, simple filing away of key value pairs in a very simple manner.
But some of you are probably asking yourselves, that simple stuff, do you have anything with a little more kick? HTML5, its contributors, and the WebKit team definitely wanted to punch it up a little too. So we bring you now the HTML5 client-side database storage. Now, if you're familiar with databases, you know that they are data warehouses. Complex data, tons of data, lots of structure, relation between different data items. That's what databases are for.
So this was actually brand new in the Safari 3.1, but is now actually going to be in the iPhone 2.0 as well. And it's a SQL-based database. So everything you know about SQL, the language only almost 30 years old now that we love, we're talking structured data, data with relations, tons of data, manipulating megabytes of data in JavaScript, not a problem, is asynchronous.
We need to be fast. We can never freeze up the UI. And we're moving this data around and there's processing involved. So you have to provide some callbacks to help move the process along. We'll see how that works. And like everything else, same origin-based security. So documents from your domain and your users are gonna be the only ones who can see your data.
And when we say SQL-based, we mean SQL. We got tables that you can create and modify and drop, and we got rows of data to insert and select and update. And there's definitely indexes when you can give the engine performance sense about your schema, and also triggers, of course, to keep data in sync. And they're definitely our transactions. In fact, they're built into the API. We won't let you forget about them.
So there's some advantages to keeping complex data on the client side. Some developers come to me and they say, you know, my data needs to live in the cloud. I've got these remote servers. This rich content needs to be there to sync back down to some other browser later or to be shared.
But that can be slow sometimes, right? So when we're keeping the data local, we're avoiding the network. The order of magnitude is slower than a client machine. And modern hardware can definitely handle the data for just one user. These massive server database backends are for thousands of users. So just one user, not a problem in the browser.
Plus, manipulating the structured data is-- it's a lot easier if you just need to do some manipulation to keep it on the client side and avoid a round trip just for that type of thing. There is some less complexity. We are avoiding that round trip. We don't have to design an XHR protocol.
We don't have to design a server backend, all that type of stuff. One example of where caching data, even though it needs to live in the cloud eventually, is like a web mail application. When you're writing an email draft and you see the little spinny start going and it's sending your draft off to the server every few seconds, that's kind of insane, right? What if we just cached it locally and sent it off when it was done? So we can avoid a lot of the complexity when it suits us. And finally, in the title of this talk, this data is offline. There is no network involved here. And there's some great advantages we can get out of that.
So this is a mail application. I've loved .magmails for years now. I use it. It's my favorite web mail application. And it's because it looks and feels and acts a lot like a native mail application. But there's something a native mail application can do that no web mail application can do until now. And that is store data offline, store messages offline. Let me write new messages, reply to messages offline, no network.
I commonly download my email to mail.app on OS X Leopard and then on the bus on the way to work where there is no network connection, I'll write new emails and they'll get shipped off when the network connection shows up. So why shouldn't a web mail application be able to do the same thing? Let's walk through an exercise real quick.
We'll see how we might augment a web mail application to do that. So if we're going to be storing data in a database, we need a database. This is an introduction to the API. There's a new global window object call open database. This is the mail database. It's an internal identifier for our script. So we come back to the mail database later at any time. It'll be the same database as here.
These two nuggets of data, the display name, expected size, these are to give the users hints in case the browser wants to prompt them about, you know, this website's going to store data. Here's why and here's how much. And the user can make an informed decision. And then you call open database. The bare minimum, though, is just having a database name. That's the important identifier.
So we have our database handle. What can we do with it? Transactions are built into the API, and that's what we do with our database handle is we open a transaction. This is also your first introduction to the callbacks. We keep things asynchronous so the browser never locks up processing this data.
Transaction callback, it'll be called when the transaction to the database is open and you're ready to go. Transaction error callback in case something really bad happens. The database will be okay and safe, but you can still be notified about it. And then the completion callback, when those bits are on the disk and the transaction is complete and you can let your users navigate away, the completion callback will be called.
So we have our open database handle. Let's start running some SQL. Many of you know SQL, and this is where you do it. I've got my query here to create a messages table inside of my mail database. Simple ID, the subject text, the recipients, and the body. And then I call execute SQL on the transaction object. And now I have a full database running inside of the web browser simply with a JavaScript interface.
And here's something else. You're probably familiar with a SQL query with arguments. You know, efficiency's sake, you can reuse the same query over and over, put different data in the query. So here I'm inserting my email message to my father. By the way, Father's Day is this Sunday, and your fathers have all called me and told me to remind you, so don't you dare forget. So I've got the email message to my father in a JavaScript array. These are the arguments to be filled in with these question mark placeholders and the second form of execute SQL to do arguments in your query.
There's a third form of execute SQL as well, a statement callback. What's good is putting data in if we can't get the data out. So this is the form you need to use to get that, and you get a SQL result set object to get information out of the database.
There's also a fourth form. An individual statement can fail. You might have mistyped the query. The schema might have changed underneath your feet, or there might be no more space left for an insert. So you can be notified about that error. Return false. Tell the browser, you know, I handled the error. Keep running this transaction. Or if you decide you want to abort it, return true, and you will jump out of the transaction. It will be safely rolled back, like any transaction in any great relational database. And that's it.
So the mail application is one example of a great web app that can be enhanced by using offline database storage. Another example I could think of is a web calendar. These are catching on too. So my esteemed colleague, Ada Chan, a fellow Safari and WebKit engineer, has cooked up a really cool demo of that. And I'd like to invite her to show it to us now.
So good afternoon. My name is Ada Chan, and today I'm going to show you a calendar web application that makes use of the HTML5 client-side database feature that Brady just talked about. So here's my calendar application. Even though this looks pretty similar to a native calendar application, I want to remind you that this is a live web application running on Safari, and it's built only on web standards, which includes HTML, CSS, JavaScript, and HTML5 client-side database. So let me show you some of the things you can do in this calendar.
You can browse the different months in the calendar by pressing the back and forth buttons. You can view details of an existing event by double clicking on it, and you can update anything in your event. For example, I'm going to change the title of today's talk to be something a little bit more exciting.
I can also create a new event. For example, I'm going to double click on today and add a reminder to go to the WWDC party later tonight. It's going to be an awesome party. It will be at Yerba Buena Gardens at 5th and Howard, San Francisco. I'm going to get there at around 6:30, leave it around 9:30. And this is a work event. And I'm going to mingle with other cool folks from WWDC. So one advantage of this being a web application is it's pretty simple to embed other web content in it. For example, I added Google Maps to my calendar application.
I can also delete an event. For example, my dinner tomorrow got canceled, so I'm gonna select it and press the delete key to delete it. So all the data you see here is stored on the client. And that's pretty nice because access to the data is really fast. And even when the user is offline, the data is still available.
So what are some of the requirements for storing this type of data? Well, as you can imagine, data for a calendar is quite structured. Each calendar event contains attributes such as the event name, the location, the start and end times. And it's pretty important to preserve the structure of this data in order to do things like fetch all the events to display for the current month.
It would be difficult to do something like that if I have to organize my data into key value pairs to be stored into things like cookies or the storage interface that we talked about earlier. So the first requirement is to preserve the data structure. Second requirement, this data needs to be persistent. My users should be able to access this calendar data years from now and it should still work.
Third requirement, we should not need to worry about any limitation on the size of this data. My users can potentially store years and years of calendar data if they want to. Well, client-side database satisfies all these requirements, and I'm going to show you how we use it in this application.
So let me fire up Xcode. And the first code example I'm going to show you is how we open the database. Well, one good practice before using client-side database is to check whether the browser supports it before using it. And one good way to do that is to check for the existence of the open database method in the global window object. And after we have done that, just call the open database method to open the calendar database. Pretty simple. Next example I want to show you is how we insert a new event into the calendar database.
What we need to do is to specify a SQL transaction callback. This is a method that's going to describe the transaction that you want to make, and it takes in one parameter, which is the SQL transaction object, named Tx here. And we need to call the execute SQL method on it with two arguments.
First, the SQL statement that we want to execute. And second is a list of arguments to the SQL statement above. And the question mark placeholder is a really good feature to use because it also prevents SQL injection attacks, something that we're all very scared of. So, and finally, we need to call the transaction method on a database object with a SQL transaction callback method that we just specified.
So the next piece I want to show you is how we fetch all the events to display for the current month. This is another example of executing a SQL statement on a database. But this time, we need to process the result from the database query too. Our execute SQL method this time takes in a third argument, which is the SQL statement callback. This is the method that's going to process the result from the database query. And it takes in two parameters, the SQL transaction object and the SQL result set. The tabular results from the database query can be accessed via the rows property in the SQL result set.
So hopefully the last two code snippets give you a pretty good idea on how to execute SQL transactions on a database. Well, there's one more thing I haven't shown you yet in this calendar application, which is-- We have search. I can easily search for all my events that contain a particular keyword. For example, here are all my WWDC events.
And next, I'm going to search for the For all my events that takes place in San Francisco, so I'm going to type in San Francisco, huh, but I don't get anything. Well, I'm pretty sure this list should not be empty because, well, WWDC is in San Francisco. So let me go to my code again and figure out what's wrong here.
So this is the SQL query that I'm using to search for all my events based on the user input. And it looks like I'm only matching the event title and the event details, but not the event location. So let me fix that really quickly by extending my condition to also check for the event location. And I have now a new argument, so I need to update the list of arguments down here. I'm going to save the file and go back to the page, reload it, and try my search again.
And boom, here are all my San Francisco events. So notice how a powerful new aspect of my search can be added just with a small code change like this. This really demonstrates the power of a relational database and the flexibility of the SQL language to manipulate that database. And this is really cool stuff.
So hopefully at this point, you're all so excited about the client-side database feature that you want to add it to your web application and try it out in Safari. But as web developers, you love debugging tools, and you wish there could be a debugging tool to help you inspect the database. Well, I hate to disappoint any of you, so I'm going to show you a new database feature in our newest and greatest Web Inspector. So let me pull up Web Inspector.
So notice we have a new panel now called Databases. It lists all the databases that are currently opened by the page. And when you expand it, it shows you a list of tables under that database. And by selecting a table, you see all the data in that table.
But let me show you something even cooler. When you select the database, A prompt shows up on the right-hand side. You can start typing in your own SQL query to inspect the database in any way you want. And it even comes with tab completion. This is really, really awesome. So I hope that was a good introduction on how to use client-side database in your web application. And now I'm going to give the stage back to Brady for the rest of the presentation.
Thanks, Ada. So that was pretty cool. What we saw was some pretty rich data being manipulated on the client side in the web browser. These events have a lot of structure, and we can have tons of these events. And the databases on the client side are not going to skip a beat.
And we also saw the power of these complex queries. The stuff, the power you're using, you're used to using on the server side when you have a database at your disposal, just manipulating this data as a piece of cake with the structured query language. That's what it was designed for. That search augmentation, for example, that was just really cool and easy. I love that. There was no plugins here, and this is all standard web technology. Probably my favorite aspects of this demo. This thing is just pure web all the way, and that's just pretty cool.
There's a few more points about the client-side databases I'd like to talk about before we move on. There is no standard HTML5 SQL dialect. If you're very familiar with databases, you're familiar with the different database engines have incompatibilities between their dialects. The standards group is very aware of this, and we hope to come up with a subset for the dialect, but there isn't one yet. Right now, in WebKit, we're using SQLite.
It's a very fast, very modern, very mature, and very stable database engine that is a piece of cake to embed, and it supports a very simple subset of most SQL dialects. So if it works in SQLite, it's probably going to work everywhere. So it's a good thing to keep in mind. We had to disable a couple things for sanity's sake. The standard says disable, begin, commit, and rollback, and we couldn't agree more. The transactions are built into the API. We're not going to let you manipulate them directly in SQL.
If you're familiar specifically with SQLite, there's these pragma statements that go behind the scenes and let you tweak the performance of SQLite and look at internal workings. We don't need to allow that. This is the web, and we should be worried about the data. And in the same vein, there's the attach and detach and virtual tables features of SQLite, which are very powerful native C API things, but we couldn't find a place for them on the web quite yet, so those are also disabled.
And in a nutshell, that is the HTML5 standard client-side databases. So these things combined with the local storage, we now have a couple great ways for the persistent, reliable, predictable storage of data on the client side. And some of you might be on the edge of your chair saying to myself, OK, I can store all this data offline. I'm getting excited. What else can I store offline? And there is something else. I mentioned the name earlier, and now we're going to go into a little more detail about the HTML5 application cache. So before we talk about that, let's talk about what an application is.
Web application, for example, has all these different resources, much like native applications these days. Media and content, like our HTML for our content, our CSS for our style, our JavaScript for our control, and all the images. And they all come together in this bundle that make up what we now call a web application. So the application cache, we're giving it to you for the first time in the Safari 4 developer preview, is a standard way to store your entire web application offline. Pretty cool stuff.
So all you need to do to get this to work with your current web application, you need to specify a manifest of resources to list every single one of these resources that your web application needs to run. Application cache also provides a mechanism for atomic updating of the application.
So, you know, in college I was working on a huge web app, had thousands of different files. Late at night, not enough caffeine, I made a few tweaks here and there, a couple different files, refresh it in the browser, and I was so confused. Because it broke. Because I kind of got this hybrid, right? Some old resources, some new resources.
So the atomic updating is going to solve that problem, keep the entire application in sync. And now that we're moving more and more things offline, we want to know when we're offline. So the application cache spec also introduces online and offline network events. You can change the behavior of your application on the fly to act like it's offline, to make different decisions.
And then you'll get the online event when a network is again. This is all I'm going to talk about this because it's so simple. You just listen for the event at event listener online or at event listener offline. That's all you need to get that to work. Okay.
So what is this manifest of resources? We call it the resource manifest, quizzically enough. It's just a text file with a couple URLs in it. Well, every URL of every resource you need, but it's just a list of these URLs in a file. They can be absolute, relative, on a different domain even. It's just a text list.
We're making it very clear that you're very specifically opting into the application cache, so you need to make a server tweak, too, and serve this text file with the cache-manifest, I'm sorry, text slash cache-manifest mime type, and the browser will recognize it as valid. So this list has two types of entries in it.
First is the explicit entry, or the cache entry. These are the critical resources to our web application that we must store offline for it to operate. When these are stored offline, the browser will ignore the network when fetching these resources and always get the locally cached copy. But then on the opposite end, we have the online whitelist entries, or the network entries. These are dynamic resources that are about up-to-date information, stock quotes, weather forecasts, that type of thing, that it wouldn't make any sense to store offline.
So this will tell the browser, treat this like a normal resource, we'll always get it from the network. When the network is available, of course. So this resource manifest, here's a quick example. Start out with cache manifest up at the top, and then list your URLs. Can see here I have my HTML, my JavaScript, my CSS, some images, even a self-portrait from a different website.
And you can also specify those two sections I was talking about. The explicit entries are also the default entries back in the list I just showed you. These are explicit offline cached entries. And then you list those network entries, the dynamic things that you always want to fetch from the network.
How do you specify your resource manifest now that you've worked really hard in this text file? Some of you are probably familiar with the HTML element. Hopefully most of you are. The loneliest and simplest and nakedest of the elements in HTML without any official standard attributes, until now. Specify the URL to your manifest file with the manifest attribute on HTML, and you've just activated the powerful method of storing your entire application offline.
Now this update process I touched on. So you turn on the application cache. Your user visits the URL of your web application. The browser says, hey, there's a manifest here. These guys are cool. I'm going to start caching this application offline. So it starts storing everything away. Now when your user revisits your site later, the browser will recognize this in the cache and load these locally stored resources. But it will also start the update process automatically if the network is available. It'll go out to your website and fetch a new manifest if it exists. And if it does, it'll start fetching the new resources as well.
Using the application cache object on the global window object, you can monitor the status of this update process. You can also listen in to a couple of events. And then when it's done, you need to add a tiny little JavaScript to actually perform the atomic update of your application.
So these are the events. There's also a status attribute you can listen to if you want. You're probably most interested in this last one here, onUpdateReady. If you just listen to it setting the attribute on application cache to your event handler, when the update is ready, you call swapCache. What this does is it throws out the old version of your application, it slides in the new version, and now you've just atomically upgraded your entire web application.
So that's the application cache in a nutshell. All of the resources that make up our website in a tidy little package make up what we call a modern web application. And that's all I said I had to talk to you about before. We're talking about offline data, offline application cache resources, and this is great.
I'm starting to get an idea, talking about data and offline application, and I feel something bubbling, and I have one more thing to talk to you about. And all of a sudden, some new possibilities are opening up here. If you've played around the Safari for Developer Preview, you might have noticed the Save As Web Application feature.
What if that application that lived on the desktop was a standalone offline web application? So many users out there launch the browser in the morning, they go and check Gmail, and that's all they do all day. 95% of their browsing is one, two, or three websites. And those users wish they could just store that little icon for what's important to them in their dock and just launch it from there. They don't care about browsing the whole internet. They'll look for I can't hash cheeseburger later at night after work. They just want to check their mail from their dock. So that's what the Save as Web Application feature in Safari 4 allows.
And I hope that we can make that web application entirely offline. It's now becoming possible. So let's bring this all together. We have our application resources offline. We have all the data our users and our application needs offline. We know when we're online and offline, so we know how to change the behavior of our application.
And what about the title and icon, important things applications and the desktop have? HTML5 even has new standards to specify a simple application title and a rich native desktop feeling icon. Let's have another demo really quick. I'd like to invite Ada back on stage. She's going to show you how to make the offline calendar into a standalone offline calendar.
Ada. Thank you again, Brady. So now I'm going to show you how to turn the online calendar web application you saw earlier into a standalone offline web application. So this is slightly different from the previous version in that it has an online indicator to show whether we're running online or offline. And this time, this is loaded from a server on the network.
So before we dive into the details, let's think about how the combination of client-side storage and application cache can be useful for this calendar application. By storing all the data on the client and using the application cache so that we have all the resources we need locally to run this application offline, now my users can access this calendar application anywhere. When they're on the bus or on the train or at the park with no network connectivity, they can still pull up the calendar, view their events, and change their events.
And when they're back online, as you can very easily imagine, this app could, in the background, update the client data with the latest from the server and sync all the user's changes back to the server. But to the users, this web application runs just as well offline, like a native application, and it's all seamless to them. So this is really cool. So now I'm going to show you all the steps we need to take to turn this application into your standalone offline web application. The first thing we need to do is to specify a resource manifest.
So here's how our manifest looks. It's very simple. This will be used by the user agent to populate the application cache. And this one only contains explicit entries, which are the resources that will be added to the offline cache and will be retrieved locally when the page loads.
So after we have our resource manifest, we need to change our HTML. We need to set a manifest attribute in our HTML element and set that to a URI of the resource manifest file. You may also want to specify a name for your application with the meta tag with name equals application dash name.
And a custom icon can also be specified for your application with the link tag with rel="icon". And with all these changes-- oh, and if you don't specify the icon, Safari will make a pretty one for you based on the screenshot of your web page. And now with all these changes, we are ready to turn this application into a standalone offline web application. My users can just go to File, Save as Web Application. Save. And now my application automatically loads, and it's its own application, this offline calendar application. And check out the dog. We have our own custom icon for this application.
And I can move this around just like another native application. And it even shows up as one of the opened applications on my app switcher when I press Command-Tab. And this is all really cool. And it's not dependent on Safari running alongside with it, so I'm actually going to quit Safari now. And my application's still there, still running. But to prove that this can really work offline, I'm going to ask Brady to come up on stage and actually cut the network cable so that we can really go offline here.
So we should see the online indicator change any time now. And it did. So now we're completely offline. I'm going to quit my application and restart it. See, all my data is still there. I can still view all my events. I can still add a new event. And I'm not even online, and things still works. So I hope that was a pretty good introduction on how to use client-side storage and the application cache to make your web application offline capable. And now I'm going to turn the stage back to Brady.
Thank you, Ada. Pretty cool. Pretty cool stuff. So what we saw was we saw this web application-based offline calendar turn into just a standalone natively integrated desktop application. It was really easy. Ada only had to add a couple of lines of code to HTML. That new application name meta attribute and the new format for that icon with the sizes, those are now in the HTML5 standard. So those are very standard ways of doing that. And yes, I cut the cable. There's no network required. This thing lives on your client's machines and runs there. It's just beautiful.
So we talked about some of the solutions you as web developers have been using to handle data. They're all great technologies. They have their place and they do some things really, really well. But there's been this gap in the standards for the browser. So HTML5 is trying to fill that.
We talked about the session storage to replace cookies and the local storage for simple, persistent key value storage, as simple as filing something away and just using JavaScript objects. And then the power of a relational, queryable database inside of the web browser. So many possibilities start to open up. It's awesome. And then the application cache. Every resource that we need to make our web application run, stored in a package. offline on our users' machines.
And then we can make a fully standalone application that works without a network based entirely on web technologies. These are just some of the really exciting directions that the web and web standards are headed. And I hope you're getting as excited as I am and you want to try some of this cool stuff out.
For more info, we'd love to hear from you at the WebKit open source project, including #WebKit, the coolest channel on Freenode. You can learn in detail about all the standards that I've talked about today at the What Working group with the HTML5 specification. Vicky Murley, who plays a mean game of tic-tac-toe, would love to answer any questions you have.
And hope you stay in this room. After this session, we're going to talk about the great new developer features in Safari 4, including a lot more about the hot new Web Inspector. And then at 5 o'clock today, we're going to have a quick lab about Offline Data. If you have any code or things you want to talk to us about, we'll be there. Now I'd like to open it up to Q&A.