Storing iPhone apps locally with data URLs
By W. Clawpaws on Monday 16 July 2007, 10:11 - iPhone - Permalink
Some people think that you need net access to run web-based applications on your iPhone. Not so. The URL below provides a simple tip calculator (sadly this crappy blogging system doesn't let me do a direct link, which sucks, but you can copy and paste and add it your bookmarks on your computer then sync with your iPhone, and/or make your own page with a direct link). By using a data: URL, the entire page content is all in the URL. If save a bookmark for this URL, you can access this little JavaScript-based app even in airplane mode.
data:text/html;charset=utf-8;base64,PGh0bWw+CjxoZWFkPgo8bWV0YSBuYW1lPSJ2aWV3cG9ydCIgY29udGVudD0id2lkdGggPSAyNDAiIC8+Cjx0aXRsZT5UaXAgQ2FsY3VsYXRvcjwvdGl0bGU+Cgo8c2NyaXB0PgoKZnVuY3Rpb24gdGlwKGFtb3VudCkgewogICAgcmV0dXJuIHRpcDsKfQoKdmFyIG91dHB1dCA9IG51bGw7CnZhciBwZXJjZW50ID0gMTguNSAvIDEwMDsKdmFyIHJ0aXBfZmFjdG9yID0gMC4yNTsKdmFyIHJ0b3RhbF9mYWN0b3IgPSAxLjAwOwoKZnVuY3Rpb24gd3JpdGVPdXQobGluZSkgewogICBpZiAob3V0cHV0KSB7CiAgICAgIG91dHB1dC5hcHBlbmRDaGlsZChkb2N1bWVudC5jcmVhdGVFbGVtZW50KCJiciIpKTsKICAgfSBlbHNlIHsKICAgICAgb3V0cHV0ID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoIm91dHB1dEFyZWEiKTsKICAgfQogICBvdXRwdXQuYXBwZW5kQ2hpbGQoZG9jdW1lbnQuY3JlYXRlVGV4dE5vZGUobGluZSkpOwp9CgpmdW5jdGlvbiB1cGRhdGUoKQp7CiAgICB2YXIgYW1vdW50ID0gTnVtYmVyKGV2YWwoaW5Gb3JtLm51bS52YWx1ZSkpOwogICAgaWYgKGFtb3VudCA9PSBOYU4pIHsKICAgICAgICB3cml0ZU91dCgnSHVoPycpOwogICAgfQogICAgdmFyIHRpcCAgPSBhbW91bnQgKiBwZXJjZW50OwogICAgdmFyIHJ0aXAgPSBNYXRoLnJvdW5kKHRpcCAvIHJ0aXBfZmFjdG9yKSAqIHJ0aXBfZmFjdG9yOwogICAgd3JpdGVPdXQoJyQnICsgYW1vdW50LnRvRml4ZWQoMikgKyAnICsgJCcgKyBydGlwLnRvRml4ZWQoMikgKyAnID0gJCcgKyAoYW1vdW50K3J0aXApLnRvRml4ZWQoMikpOwogICAgdmFyIHJ0b3RhbCA9IE1hdGgucm91bmQoKGFtb3VudCArIHRpcCkgLyBydG90YWxfZmFjdG9yICsgMC4yNSkgKiBydG90YWxfZmFjdG9yOwogICAgd3JpdGVPdXQoJyQnICsgYW1vdW50LnRvRml4ZWQoMikgKyAnICsgJCcgKyAocnRvdGFsLWFtb3VudCkudG9GaXhlZCgyKSArICcgPSAkJyArIHJ0b3RhbC50b0ZpeGVkKDIpKTsKICAgIAp9CgpmdW5jdGlvbiB6YXAoZmllbGQpIHsKICAgIGlmICghIGZpZWxkLnphcHBlZCApIHsKICAgICAgICBmaWVsZC56YXBwZWQgPSB0cnVlOwogICAgICAgIGZpZWxkLnZhbHVlICA9ICIiOwogICAgfQp9Cgo8L3NjcmlwdD4KCjwvaGVhZD4KPGJvZHk+Cgo8aDE+VGlwIENhbGN1bGF0b3I8L2gxPgoKPHA+CkVudGVyIGFmdGVyLXRheCB0b3RhbDoKPC9wPgo8Zm9ybSBuYW1lPSJpbkZvcm0iIG9uU3VibWl0PSJ1cGRhdGUoKTsgcmV0dXJuIGZhbHNlOyI+CjxpbnB1dCB0eXBlPSJ0ZXh0IiBuYW1lPSJudW0iIG9uRm9jdXM9InphcCh0aGlzKSIgdmFsdWU9IjI0LjM3IiAvPgo8aW5wdXQgbmFtZT0ic3VibWl0IiB0eXBlPSJidXR0b24iIG9uQ2xpY2s9InVwZGF0ZSgpOyIgdmFsdWU9IlRpcCIgLz4KPC9mb3JtPgoKPHAgaWQ9Im91dHB1dEFyZWEiPgo8L3A+Cgo8L2JvZHk+CjwvaHRtbD4=
By putting images inline using data: URLs, you can create pretty rich pages and store them locally. I created a 363,488 byte URL for my home page (complete with images) and it loaded just fine on my iPhone.
Here's a quick Perl one-liner to turn HTML into a data: URL.
perl -0777 -e 'use MIME::Base64; $text = <>; $text = encode_base64($text); $text =~ s/\s+//g; print "data:text/html;charset=utf-8;base64,$text\n";'
By making these links programmatically, you even have an ugly hack to do persistent storage on the iPhone. Just encapsulate your app and its state in its URL.
Comments
Looks like a cool way to use something like http://tiddlywiki.com/
Fantastic. I'm sure I'm being a newb about this, but I find it mind-blowing.
Damn, this is a claver one. Congrats!
For the relatively uninformed--is there a way to take an existing iPhone "app" page and do this quickly? I think you'd have a line out your door of people waiting for a quick way to embed apps in this manner--I'd love to have some games on my iPhone this way.
Thanks!
Here is a bookmarklet that will grab the current page's source, generate a data: URL, and load it into the browser, which can then be saved as a bookmark:
javascript:x=new XMLHttpRequest();x.onreadystatechange=function(){if(x.readyState==4)location='data:text/html;charset=utf-8;base64,'+btoa(x.responseText)};x.open('GET',location);x.send('');
Note that scripts and stylesheets must be embedded in the page, not linked. Also, as you mentioned above, images should be embedded using data: URLs as well.
This is fantastic! I had no idea this was possible. What is the technical limitation (length) of such a URL?
Thanks a lot.
Chris
Brilliant... very nifty. Cheers for posting this.
I can see Javascript libraries being coded around this feature in no time.
that may help:
http://software.hixie.ch/utilities/...
http://en.wikipedia.org/wiki/Data:_...
"Note that scripts and stylesheets must be embedded in the page, not linked. Also, as you mentioned above, images should be embedded using data: URLs as well."
What we need now is a sortof cross between wget(1) and ld(1), something that will take an HTML file, read the links in it (like "link symbols" in an object file) and resolve them into data, replacing the links with the data at the other end -- essentially "static linking" the linked data into a single page. For many SPAs, it would be enough to simply resolve the <head> links.
One more thought on this: this also works for saving Word Docs and Excel files in bookmarks too, since the iPhone uses plugins to render these in Web Views. All you have to do is use the appropriate mime type for the document you are encoding:
mimetype = case file.split('.').last.downcase
when 'pdf'
'application/pdf'
when 'html' || 'htm'
'text/html'
when 'doc'
'application/msword'
when 'xls'
'application/vnd.ms-excel'
end #case
Further, you can take a web page in safari and save it as a "web archive." Web archives can then be converted to word documents (thus embedding everything) using textutil(1) from the command line (on OS X systems)
As far as the maximum length of data: URL Safari will allow, I haven't tested beyond the 350K URL I made for my home page. That strikes me as fairly huge. Clearly Safari (and Firefox) go beyond the requirements of RFC 2397, which claims that data: URLs can only be short because of various sundry HTML limits, namely:
One other thing, as I alluded to in my original post, it is really easy to make a program that edits its own data: URL. You can fetch and change the URL via the location variable, and decode/encode it via atob/btoa.
in PHP, use $data = base64_decode($data); to convert to base64 for images.
I'm wondering if this is something that should be implemented for IUI? Embed the framework, AJAX the content?
Here is a quick development package, all in the URL ;-)
data:text/html;base64,PGh0bWw+CjxoZWFkPgo8dGl0bGU+RGF0YSBVUkwgRGV2ZWxvcGVyPC90aXRsZT4KPHNjcmlwdD4KdmFyIGdlbmJ1dHRvbiA9ICI8YnV0dG9uIG9uY2xpY2s9XCJkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgndXJpJykuaW5uZXJIVE1MPW1ha2VEYXRhTGluaygnVVJMJywgZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ3RleHQnKS52YWx1ZSk7XCI+R2VuZXJhdGUgREFUQSBVUkw8L2J1dHRvbj4iOwpmdW5jdGlvbiBtYWtlRGF0YUxpbmsgKGxpbmssIGhyZWYpIHsgCiAgcmV0dXJuICc8YSB0YXJnZXQ9Il9ibGFuayIgaHJlZj0iZGF0YTp0ZXh0L2h0bWw7YmFzZTY0LCcrd2luZG93LmJ0b2EoaHJlZikgKyAnIj4nICsgbGluayArICc8L2E+JzsKfQpmdW5jdGlvbiBmZXRjaERhdGFMaW5rIChsaW5rKSB7CiAgdHJ5IHsKICAgIHZhciBtID0gbGluay5tYXRjaCAoL14oKGRhdGE6fGNoYXJzZXQ9KVteO10rOykqKGJhc2U2NCwpPyguKykkL2kpOwogICAgcmV0dXJuIGF0b2IobVs0XSk7CiAgfSBjYXRjaCAoZSkgewogICAgLy8gYWxlcnQgKGUgKyAiXG4iICsgbGluayArICdcbiInICsgbS5qb2luICgnIlxuIicpICsgJyInKTsKICAgIHJldHVybiAnJzsKICB9Cn0gCjwvc2NyaXB0Pgo8L2hlYWQ+Cjxib2R5IG9ubG9hZCA9ICIKICBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgndGV4dCcpLnZhbHVlID0gZmV0Y2hEYXRhTGluayh3aW5kb3cubG9jYXRpb24uaHJlZik7CiAgZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ3VyaScpLmlubmVySFRNTD1nZW5idXR0b247CiAgZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoJ3VyaScpLmZpcnN0Q2hpbGQub25jbGljaygpOyI+CjxpbnB1dCBpZD0ndXJsJz4KPGJ1dHRvbiBvbmNsaWNrPSJkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgndGV4dCcpLnZhbHVlID0gZmV0Y2hEYXRhTGluayhkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgndXJsJykudmFsdWUpOwogIGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCd1cmknKS5pbm5lckhUTUw9Z2VuYnV0dG9uOwoiPkRlY29kZSBEYXRhIFVSTDwvYnV0dG9uPgo8aHIvPgo8dGV4dGFyZWEgaWQ9InRleHQiIAogIGNvbHM9IjUwIiAKICByb3dzPSIyMCIKICBvbmtleWRvd249ImRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCd1cmknKS5pbm5lckhUTUw9Z2VuYnV0dG9uIj4KPC90ZXh0YXJlYT48YnIvPjxici8+CjxwIGlkPSJ1cmkiPjwvcD4KPC9ib2R5Pgo8L2h0bWw+
When opening this link, the text area will show the html/script source of the page.
Entering a data url into the upper input element and clicking on the decode button will decode the URL into the lower text area where you can edit it.
When the text area is changed, the URL link below it changes to a Generate button which, when clicked, will convert the text area into a Data URL
Clicking the URL link will open the data URL in a new window.
Developed and tested on Ubuntu/firefox, let me know if there are problems on other systems
Enjoy...
Could this data URL hack be a way to embed html versions of PDF files into Safari and look and zoom them via the browser instead of using the poor email attachment capacity without landscape mode?
Regarding storing a PDF file in a data: URL, yes, you can. You can encode any kind of content that Safari can render. Whether its worth going to the trouble is another question though.
I successfully encoded the entire first volume of Gibbon's Decline and Fall to PDF and moved it to my iPhone. It even opens it, though Safari generally crashes after a few minutes of scrolling down pages. The PDF I made was 1.2 Megs.
Also, Safari on my Macbook would beachball for minutes at a time when I'd try to copy/paste or move the data URL around the bookmark page. I had to use the Property List editor to add the data: URL into my bookmarks with any speed.
OK, guys, for non-programmers, where do you put the code to make it work - in plain English, please. Thanks.
Thanks fellows for the tips, here is the non geek version optimized for PDF storing and reading via Safari. Long live the landscape mode: http://pimm.wordpress.com/2007/07/1...
Not to put too fine a point on it, but that's actually a 6-line perl script.
To respond to “GQB”'s claim that my Perl one-liner is a six liner: splitting hairs over this kind of stuff seems pretty silly to me, but hey. FWIW, while it's true my original wasn't one statement, it nevertheless was one (long) line. When I type code into the shell on one line, I (and others) call it a one liner.
In any case, the Perl script would be better written to do all its real work in one statement, as follows:
So, the original is hardly a model of perfection, but I when wrote it, I was just writing a snippet of Perl in a few seconds at the shell prompt. Good enough was good enough.
Bear in mind that apps running via the data: protocol do not appear to preserve cookies. I use a cookie for preferences in my tip calculator, which means in the data: version, you have to reset your preferences (if they're not the default) each time you load the app. I made a data: version a few days ago, which you can load from the second link at http://dannyg.com/iphone.
This sounds like a fantastic breakthrough. Can't wait for it to work, especially for web pages. For me, PDFs aren't as important but also sound great.
# 5 David, you posted the code to make this work. It is not functioning on my iPhone or Mac's Safari. Somehow the spacing is not going into a link right for me. But x=new has a space before the XML and any combo of no space or percent20 space added in the URL if left alone don't seem to do anything for me. More explanation would be fantastic.
After seeing this post, a friend and I put together some scripts in a really easy to use package - drop a file on it, and you get a bookmark you can sync with your phone!
We called it Filemark Maker, and It's at http://www.insanelygreattees.com/ne...
The Wikipedia article on data: URIs links to this fine online data: URI generator:
http://www.scalora.org/projects/uri...
Use "HTML" mode and copy the resulting anchor href value or img src value.
>> Here is a quick development package, all in the URL ;-)
(see hansp post of 17 July 2007)
This script allowing modify and save for bookmarks
is clever. I could only get it to function
on Firefox, not the iphone. Anyone come up with
a method of editing data url pages on the iphone?
Thanks for the inspiration of the one-liner. That led me to waste most of the afternoon completing the task. http://www.somewhere.com/url2iphone... will let you enter the URL of a web site and get back a complete package--images, css and javascript--as a single data URL. I'm sure it's buggy, but feel free to try it out.
Sorry, I have very limited programing experience but plenty of alcohol. I just want to play freaking shredder chess offline. Can someone tell me how to do it step by step for someone that is good at following detailed instructions, but not that program savvy? I also am in the process of converting to mac entirely, but still only have a PC at home.
I have tested the data: URL functionality on the iPhone. If you have read the documentation on the data: URL stuff (see RFC 2397) you will note warnings about length of the <data> stream. I've done some testing tonight and the limit on Safari 3.0 on the iPhone is 128K. This is enough for some fairly sophisticated app development using HTTP, XML, CSS, and Javascript and especially the xmlhttprequest object in JavaScript, but that's about it.
I have found two interesting sources ( http://filesfinds.com & http://fileshunt.com ) and would like to give the benefit of my experience to you.
I am not very great at these types of things. [totally illiterate.] but could someone explain how to do that, in simpler terms that a slow person like me could undertand thanks in advance. :)
sorry for sounding like a total idiot - what is the javascript / php or html equivalent so you can retrieve data from a bookmark ?
$_GET .. something #elitecss ? lets say my bookmark is called "elitecss" which has url address of an entire css file. anyone ? an (detailed) example would be great
I'm trying to figure out a way to use cookies with this. Any suggestions on how I can set the domain property of a cookie to be any url that starts in "data:" using javascript? That way you could pass data between two different data urls
hansp's Data URL Developer above (very clever) requires scrolling within the text area; use TWO fingers to scroll content inside of the text area.
http://furbo.org/2007/07/16/iphone-...
this is so nice...
Actually, the bookmark thing does work. David posted this and it works, it just sometimes takes a while or won't work on specific sites...
javascript:x=new XMLHttpRequest();x.onreadystatechange=function(){if(x.readyState==4)location='data:text/html;charset=utf-8;base64,'+btoa(x.responseText)};x.open('GET',location);x.send('');
your blos is very nice! Thank you so much for responding and answering some of our questions!!!!
hank you very much for responding and answering some of our questions!
Great instructions. I found this method to be a little awkward when uploading multiple files like pdfs to read with safari. I managed to do this without emailing or uri tricks. Check out my tutorial here : http://codytaylor.org/?p=13984
I really appreciate you offering this great solution. I have already created one application for myself but I am having a problem with a new application that has a XMLHttpRequest. The function appears to work up to the point where the call is made. After that it seems to exit the function. All functions that are called by buttons to do the XMLHttpRequest do the same. Can someone help?
This one will generate a skype speed dial:
data:text/html;base64,PGh0bWw+DQo8aGVhZD4NCjwvaGVhZD4NCiAgPHNjcmlwdCB0eXBlPSJ0ZXh0L2phdmFzY3JpcHQiPg0KICAgIA0KICBpZiAod2luZG93LmxvY2F0aW9uLmhyZWYuaW5kZXhPZigidXRmLTgiKSA9PSAtMSl7DQogICAgICAgIHZhciBza3lwZW5tID0gcHJvbXB0KCJQbGVhc2UgZW50ZXIgYSBza3lwZSBuYW1lIik7DQogICAgICAgIA0KICAgICAgICBpZiAoc2t5cGVubSAhPSBudWxsKSB7DQogICAgICAgICAgdmFyIHBhZ2UgPSAgIjxodG1sPjxib2R5PjxzY3IiKyJpcHQgdHlwZT0ndGV4dC9qYXZhc2NyaXB0Jz4iOw0KICAgICAgICAgIHBhZ2U9cGFnZSArICJ3aW5kb3cubG9jYXRpb24uaHJlZj0nc2t5cGU6IiArIHNreXBlbm0gKyAiP2NhbGwnIjsNCiAgICAgICAgICBwYWdlPXBhZ2UgKyAiPC9zY3IiKyJpcHQ+PC9ib2R5PjwvaHRtbD4iOw0KICAgICAgICAgIA0KICAgICAgICAgIHZhciBkYXRhID0gImRhdGE6dGV4dC9odG1sO2NoYXJzZXQ9dXRmLTg7YmFzZTY0LCIgKyBidG9hKHBhZ2UpOw0KICAgICAgICAgIA0KICAgICAgICAgIHdpbmRvdy5sb2NhdGlvbi5ocmVmID0gZGF0YTsNCiAgICAgICAgICANCiAgICAgICAgfQ0KICB9DQoNCiAgPC9zY3JpcHQ+DQogIA0KPC9odG1sPg0KDQo=
It will initially launch the skype call, but you can then go back to safari and create a home screen bookmark.
Really just a convoluted way of getting around the inability of the iphone to save 'skype:username?call' links to the home screen.
Interesting post, for those of you that don't have Perl or simply want an online solution to generate these data URLs, you can use this one that is specifically made for iPhone Web Apps: http://bit.ly/6vgnDJ
Hope it helps!
Gm... properly speaking I like this simple tip calculator!
I've found that both GoodReader and ReaddleDocs, which I use to read PDFs on my iPad, can display webarchive files. So, if I want to be able to view content on my iPad--for example, W3C specs--I go to them in Safari on my Mac. I then save the complete page as a webarchive and then copy it to my iPad. Both GoodReader and ReaddleDocs allow you to connect to their local storage on your iPad and copy files to it. Then just open the web archive and read it.
I've not experimented with content that spans more than one page, such as a spec with an index page and separate pages for chapters. Plus, these two apps don't provide a full web experience; for example, there is no back button. You can click on a link but not return to your original spot.
All-in-all, though, this seems like a good starting point.
Kevin
This post is quite old yet looks like it still gets a lot of google hits. Here's a small improvement to David's bookmarklet that does not choke on "illegal" characters (mostly on UTF-8 pages):
javascript:x=new XMLHttpRequest();x.onreadystatechange=function(){if(x.readyState==4)location='data:text/html;charset=utf-8;base64,'+btoa(unescape(encodeURIComponent(x.responseText)))};x.open('GET',location);x.send('');
I wonder if Apple ever decides to allow for proper saving of files...
I think it would be awesome to develop apps like this using Google Web Toolkit, but it always generates multiple files. Anyone know of a good tool to combine resources into a single html file?
Note that this technique is mostly obsolete since support for the HTML5 manifest attribute for offline apps was added to Mobile Safari
Когда в последний раз вы слышали свое дыхание?
Hi, I do think this is an excellent site. I stumbledupon it ;
) I'm going to come back yet again since I book-marked it. Money and freedom is the greatest way to change, may you be rich and continue to guide others.
Have you ever considered about adding a little bit more than just your articles?
I mean, what you say is valuable and everything.
But imagine if you added some great visuals or videos to give your posts more,
"pop"! Your content is excellent but with pics and video clips, this blog could certainly be one of the best in its niche.
Excellent blog!