Author: jsundman
Date: 2007-09-24 20:08:15 -0700 (Mon, 24 Sep 2007)
New Revision: 6577
Modified:
labs/newsmatch/iphone-openlaszlo.blog.html
Log:
copyedits to Ben's excellent blog post about the amazing newsmatch
Modified: labs/newsmatch/iphone-openlaszlo.blog.html
===================================================================
--- labs/newsmatch/iphone-openlaszlo.blog.html 2007-09-25 01:32:14 UTC (rev
6576)
+++ labs/newsmatch/iphone-openlaszlo.blog.html 2007-09-25 03:08:15 UTC (rev
6577)
@@ -2,96 +2,93 @@
<body>
<h1>iPhone Development with OpenLaszlo</h1>
+<!--I've made Bret a co-author, so no 'Bret and I; -->
<p>
-At <a href="http://www.ajaxworld.com/" title="AJAXWorld Conference &
Expo">AjaxWorld</a> tomorrow, I'll be <a
href="http://www.ajaxworld.com/general/sessiondetail0907.htm?id=143"
title="Sept. 2007 Session Description @ AJAXWorld Conference & Expo">giving a
talk</a>, with Bret Simister, co-founder
-of Laszlo Systems, about how we built an iPhone application in OpenLaszlo
+At <a href="http://www.ajaxworld.com/" title="AJAXWorld Conference &
Expo">AjaxWorld</a> tomorrow, we'll be <a
href="http://www.ajaxworld.com/general/sessiondetail0907.htm?id=143"
title="Sept. 2007 Session Description @ AJAXWorld Conference & Expo">giving a
talk</a> about how we built an iPhone application in OpenLaszlo
at iPhoneDevCamp a few months ago.
</p>
-First of all, the iPhone application is
+The iPhone application is
<a href="http://labs.openlaszlo.org/ipdc/awip03/"
target="newsmatch">NEWSMATCH</a>.
-It presents some items from a Yahoo! News RSS feed, with images and titles.
-The challenge is to match an image with the headline it illustrates. Click
-(or tap) on an image, click (or tap) on a headline, and if you match, you'll
-be given the opportunity to read the story.
+It's a game that presents items from a Yahoo! News RSS feed, with images and
titles. The challenge is to match an image with the headline it illustrates. To
play, you click(or tap) on an image, then click (or tap) on a headline. If they
match, you're given the opportunity to read the story.
-Bret and I wrote this app from scratch in two days, using Open Laszlo,
-and had it running on the iPhone with very little modification from the
-very beginning. In this article, I'll walk you through the progression
-of the app from an empty application to (almost) the finished product,
+We wrote this app from scratch in two days, using OpenLaszlo.
+It ran on the iPhone, with very few iPhone-specific modifications, from the
+very first versions.
+
+In this article, we'll walk you through the progression
+of NEWSMATCH from an empty application to (almost) the finished product,
highlighting how OpenLaszlo facilitates rapid development of iPhone
apps.
OpenLaszlo is an XML and JavaScript platform for creating
-runtime-independent rich internet applications. Right now, an OpenLaszlo
-application can run in Flash Player, or in DHTML, in most modern
-browsers. Yes, even IE6. More importantly for this article, OpenLaszlo
-applications run just fine in Safari. When Apple announced that the
-SDK for iPhone application development was going to be (just) Ajax,
-we were very excited to try out an OpenLaszlo app on the iPhone. It
-worked -- our strategy of producing high-performance standards-compliant
-DHTML was succeeding.
+runtime-independent rich internet applications. An OpenLaszlo
+application can be compiled to run in Flash Player, or in browser-native
DHTML, in most modern browsers. Yes, even IE6. More importantly for this
article, OpenLaszlo applications run just fine in Safari. Which means that, in
theory, they should more-or-less work on the iPhone.
-From this point on, if you want to follow along, get a recent nightly
+When Apple announced that the SDK for iPhone application development was going
to be (just) Ajax, we were very excited to build an OpenLaszlo application for
it. We went to the boot camp with every hope that building an iPhone
application would be as easy as building any other OpenLaszlo application, but
we were aprehensive too--what if it didn't work? It did work, as you'll see--
proving that our strategy of producing high-performance standards-compliant
DHTML was succeeding.
+
+(From this point on, if you want to follow along, get a recent nightly
build of OpenLaszlo 4, after revision 6568. (As of this writing,
the nightly build isn't available yet.)
<a href="http://download.openlaszlo.org/nightly/">Nightly builds are available
here.</a>
-The code I'm using is available in
+The NEWMATCH code is available in
<a href="http://svn.openlaszlo.org/labs/newsmatch">our source code
repository</a>.
-(For the purposes of this article, I've rewritten the progression to be
-cleaner and easier to follow, but kept the same development approach and
patterns.
-If you want to see the actual hour-by-hour development, check the svn log
+
+For the purposes of this article, we've rewritten the progression to be
+cleaner and easier to follow, but kept the same development approach and
patterns. If you would like to see the actual hour-by-hour development as it
happened, check the svn log
from <a href="http://www.openlaszlo.org/svn/sandbox/ben/smush/"
-title="Revision 6569: /sandbox/ben/smush">my sandbox</a>.)
+title="Revision 6569: /sandbox/ben/smush">Ben's sandbox</a>.)
-<h2>hello, iPhone</h2>
+<h2>Hello, iPhone</h2>
<a
href="http://svn.openlaszlo.org/labs/newsmatch/hello/hello_iphone.lzx">(source)</a>.
<a href="http://labs.openlaszlo.org/ipdc/hello/" target="newsmatch_app"
title="hello, iPhone">(live)</a>
-This is just a simple, four-line application to show that you can get
-something running on the iPhone very quickly. To run it on the iPhone,
-you'll want to just run the application, without the developer console.
-(The developer console is the purple bar at the bottom of an OL
-application page with checkboxes for runtime, debug, and other options.)
-To get just the app without the console, add query args
-<tt>lzr=dhtml&lzt=html</tt> to your url. For instance, I run my
-hello, iPhone app at the url:
+To get started, we wrote a simple, four-line application to prove to ourselves
that we could get something running on the iPhone very quickly. To run it on
the iPhone, we invoke it:
+
<tt>http://localhost:8080/trunk/demos/newsmatch-labs/hello/hello_iphone.lzx?lzr=dhtml&lzt=html</tt>
-(I've also included index.html in the newsmatch source, which has links to
+
+Notice the query arguments
+
+<tt>lzr=dhtml</tt>
+
+means that the Laszlo Runtime is to be dhtml -- this tells the compiler which
output format to translate the source to.
+
+<tt>lzt=html</tt>
+
+means that the "wapper" for the application should be simple HTML.
+
+When you're developing, you generally want the wrapper to include the
Developer's Console. The Developer's Console is a bar at the bottom of an
OpenLaszlo application page with checkboxes for runtime, debug, and other
options. It's the default wrapper, so if you don't include an lzt query
argument, that's what you get.
+
+(We have also included index.html in the newsmatch source, which has links to
all the examples in this article, with the correct query arguments.)
-<h2>"tap to begin"</h2>
+<h2>"Tap to begin"</h2>
<a
href="http://svn.openlaszlo.org/labs/newsmatch/progression01/blank.lzx">(source)</a>.
<a href="http://labs.openlaszlo.org/ipdc/progression01/"
target="newsmatch_app" title="progression01">(live)</a>
-The next thing we did is to create a startup experience. We know the iPhone
has
-pretty slow data transfer when running over EDGE, so we want to give the user
something
-to look at fast. We start the app with a kind of cover (the view named "cover")
-in front of where the actual content will go, and we show the OpenLaszlo
spinner splash.
-When the app is fully instantiated, it will execute the script at the bottom
of the
+The next thing we did was to create a startup experience. We knew the iPhone
had
+pretty slow data transfer when running over EDGE, so we wanted to give the
user something to look at right away. So, we started NEWSMATCH with a kind of
cover (the view named "cover") in front of where the actual content would go.
When the program loads it shows the OpenLaszlo spinner splash.
+
+When the app is fully instantiated, it executes the script at the bottom of
the
main lzx file:
<pre>
canvas.cover.tapToBegin.setVisible(true);
</pre>
-...which shows the text object that says "tap to begin." That little dance
means that
-the user doesn't see "tap to begin" until the app is ready to interact.
+...which shows the text object that says "tap to begin." This little dance
means that the user doesn't see "tap to begin" until the applicatoin is
actually ready to interact.
<p>
- When the user eventually does hit "tap to begin", the <tt>cover</tt> view
catches the click,
- and starts the <tt>coverslide_anm</tt>, which slides the two pieces of the
cover offscreen,
- revealing the application content underneath. That animator, in turn,
hides the "tap to begin"
- text when it starts animating, and hides the entire cover when it's done
animating. <em>Whew!</em>
- That's a bit of a complex process just to get started, but it is really
just a few lines of code
- that creates a "lively" feeling.
+When the user eventually does hit "tap to begin", the <tt>cover</tt> view
catches the click, and starts the <tt>coverslide_anm</tt>. This is what we call
an <i>animator</i>, a nifty sort of controller for visual transitions. The
animator slides the two pieces of the cover offscreen, revealing the
application content underneath. That animator, in turn, hides the "tap to
begin" text when it starts animating, and hides the entire cover when it's done
animating.
+
+That's a bit of a complex process just to get started, but it is really just a
few lines of code that creates a "lively" feeling.
</p>
<h2>Feed me! Bind me!</h2>
<a
href="http://svn.openlaszlo.org/labs/newsmatch/progression02/main02.lzx">(source)</a>.
<a href="http://labs.openlaszlo.org/ipdc/progression02/"
target="newsmatch_app" title="progression02">(live)</a>
<p>
- The next thing we did was to find some tasty data, and show it. OpenLaszlo
makes this
- sooo easy and tasty; it's one of my favorite things to do. We grabbed
+ The next thing we did was to find some data, and show it. OpenLaszlo makes
this
+ sooo easy and tasty; it's one of our favorite things to do. We grabbed
<a href="http://rss.news.yahoo.com/imgrss/441" title="Photo Highlight on
Yahoo! News Photos">an RSS feed from
- Yahoo! News</a>; this one has photos for every item. (For development, I
grabbed this feed once,
- saved it locally as an xml file, then served it up myself; there's no
reason to abuse Yahoo's servers.)
+ Yahoo! News</a>; this one has photos for every item. (For development, we
grabbed this feed once,
+ saved it locally as an xml file, then served it up statically; there's no
reason to abuse Yahoo's servers.)
Then we created a tiny class
to visually represent an item in the rss feed:
<pre>
@@ -101,33 +98,35 @@
</class>
</pre>
<p>
- The best part of that little class is
<b><tt>datapath="title[1]/text()"</tt></b>. That, my friends, is an XPath
- query saying, <em>"Give me the text from the first "title" node in the
data associated with this rssitem."</em>
- Which rss item? Well, we create an instance of one of these nerds by
<b>binding</b> it to another XPath query.
- The simplest way to do this in lzx is
+ The best part of that little class is
<b><tt>datapath="title[1]/text()"</tt></b>. That is an XPath
+ query that says, <em>"Give me the text from the first "title" node in the
data associated with this rssitem."</em>
+ Which rss item? Well, we create an instance of one of them by
<b>binding</b> it to another XPath query.
+ The simplest way to do this in lzx is by binding a datapath to an object
<pre>
<rssitem datapath="rss:/rss/channel/item[1]" />
</pre>
which means <em>"Create an instance of the rssitem class, and associate it
with the first item in the rss dataset."</em>
</p>
<p>
- Of course, we've got more than one <tt>rssitem</tt>. OpenLaszlo's
<b>implicit replication</b> shines here. I want
- to make an instance of <tt>rssitem</tt> for each of the first 12 items in
the rss feed, so, I just <em>make my
+Now here, a subtle problem arises. Since we have not seen the data, we don't
know how many records will match our xpath query, which means that we don't
know how many <tt>rssitem</tt>s we have. It could be zero, one, or several. Not
knowing what will come, how do we plan for it?
+
+OpenLaszlo's <b>implicit replication</b> shines here. We want
+ to make an instance of <tt>rssitem</tt> for each of the first 12 items in
the rss feed, so, we just <em>make my
XPath query ask for the first 12 items:</em>
<pre>
<rssitem datapath="rss:/rss/channel/item[1-12]" />
</pre>
- I throw in a layout to arrange these twelve items in a column:
+ We throw in a layout to arrange these twelve items in a column:
<pre>
<simplelayout spacing="3" axis="y" />
</pre>
- and I've got progression02, (TODO: image!) the second stage in my
application development.
+ and we've got progression02, (TODO: image!) the second stage in our
application development.
</p>
<h2>Pretty pictures, please?</h2>
<a
href="http://svn.openlaszlo.org/labs/newsmatch/progression03/main03.lzx">(source)</a>.
<a href="http://labs.openlaszlo.org/ipdc/progression03/" title="progression03"
target="newsmatch_app" >(live)</a>
-<p>Next I want to display the nice images that Yahoo provided, associated with
each item. We've already
- seen how data-binding can do XPath queries for us; I apply the same
pattern to create a thumbnail
+<p>Next, we wanted to display the nice images that Yahoo provided, associated
with each item. We've already
+ seen how data-binding can do XPath queries for us; we now apply the same
pattern to create a thumbnail
and bind it to the url specified in the feed:
<pre>
<view name="thumbnail" bgcolor="0x888888"
source="$path{'content/@url'}"/>
@@ -137,10 +136,10 @@
<a
href="http://svn.openlaszlo.org/labs/newsmatch/progression04/main04.lzx">(source)</a>.
<a href="http://labs.openlaszlo.org/ipdc/progression04/" title="progression04"
target="newsmatch_app" >(live)</a>
<p>
- I want to show some more information about these items, more than just a
few words from their title, so
- I create a view to display details about one item at a time. I give it an
id, <tt>gDetails</tt>, so that
- I can address it from any scope. I use datapaths to bind it to a
particular item in the rss feed; for now,
- I just choose the third item.
+ We wanted to show some more information about these items, more than just a
few words from their title, so
+ we create a view to display details about one item at a time. We gave it
an id, <tt>gDetails</tt>, so that
+ we could address it from any scope. We used datapaths to bind it to a
particular item in the rss feed. For now,
+ we just choose the third item.
<pre>
<!-- view to hold details about the item of interest -->
<view id="gDetails" y="150"
bgcolor="0xFFFFFF" width="$once{parent.width}"
height="120"
@@ -162,11 +161,10 @@
<a
href="http://svn.openlaszlo.org/labs/newsmatch/progression05/main05.lzx">(source)</a>.
<a href="http://labs.openlaszlo.org/ipdc/progression05/" title="progression05"
target="newsmatch_app">(live)</a>
<p>
- Around this point in iPhoneDevCamp, Bret (fearless designer) came back
from getting a bagel, and I went off in search of
- coffee. (This was when I discovered <a
href="http://sbshine.net/blog/2007/07/philz-coffee.html" title="shinyblog:
Philz Coffee">Philz Coffee</a>.) Bret started to make things look nicer, by
writing a custom layout, picking colors, sizing fonts, that sort of thing.
(Actually the code you're seeing here is me, Ben, <em>pretending</em> to be
Bret and make it less ugly, but mostly failing. Bret really wrote a new layout
with animation, and arranged everything pixel-perfect; for this article, I'm
approximating our process.) To make arrangement easier, we broke up the
<tt>rssitem</tt> class into a <tt>rssimage</tt> class and a <tt>rsslabel</tt>
class.
+ Around this point in iPhoneDevCamp, we took a break and went off in search
of coffee. (This was when we discovered <a
href="http://sbshine.net/blog/2007/07/philz-coffee.html" title="shinyblog:
Philz Coffee">Philz Coffee</a>.) We started to make things look nicer, by
writing a custom layout, picking colors, sizing fonts, that sort of thing. (The
code you're seeing here is somewhat simplified, to be easier to follow. At the
iPhone camp we wrote a new layout with animation, and arranged everything
pixel-perfect. That's another cool thing about LZX; you can create your own
layouts of arbitrary complexity. For this article, we've approximated our
process.) To make arrangement easier, we broke up the <tt>rssitem</tt> class
into a <tt>rssimage</tt> class and a <tt>rsslabel</tt> class.
</p>
<p>
- Also at this stage, we added a bit of interaction: click on a thumbnail
image, and the <tt>gDetails</tt> view will show the title and description and a
larger image from that item. That was another cool trick here; we added an
<tt>onclick</tt> handler to the rssimage:
+ Also at this stage, we added a bit of interaction: if you click on a
thumbnail image, the <tt>gDetails</tt> view shows the title and description and
a larger image from that item. That was another cool trick here; we added an
<tt>onclick</tt> handler to the rssimage:
</p>
<pre>
<class name="rssimage" >
@@ -189,26 +187,26 @@
and <tt>description</tt> views update their datamapped attributes.
<em>Wild, huh?</em> This dynamic do-what-I-mean interaction between datapaths
and user events and view attributes is a big part of making OpenLaszlo such a
fun platform for developing mashups.
</p>
<p>
- Notice, also, how I'm writing methods and creating attributes on objects
<em>or</em> classes, or even in the global scope.
+ Notice, also, how we have been writing methods and creating attributes on
objects <em>or</em> classes, or even in the global scope.
In a strict
class-based language, you'd have to create a new class in order to have a
singleton <tt>gDetails</tt> view, and
you'd have to do some tricks to make sure that you only ever had one
instance of it.
With the lzx class model,
- I can just reach in and add methods whereever I want. I could also turn an
instance into a class very easily, if
- I decide I want more than one details view.
+ you can just reach in and add methods whereever you want. You can also
turn an instance into a class very easily, if
+ you decide you want more than one details view.
</p>
<p>
- I'm also very loose about typing; I actually have no
- idea what the type of <tt>this.datapath.p</tt>, which I pass as an
argument to <tt>gDetails.show()</tt>, but I
- don't have to know; it just has to do the right thing when I pass it as a
parameter to <tt>this.datapath.setPointer()</tt>.
- The ruby folks call this "duck typing." I just call it convenient.
+ We have also been very loose about typing. We actually have no
+ idea what the type of <tt>this.datapath.p</tt>, which we pass as an
argument to <tt>gDetails.show()</tt>, but we
+ don't have to know; it just has to do the right thing when we pass it as a
parameter to <tt>this.datapath.setPointer()</tt>.
+ The ruby folks call this "duck typing." We just call it convenient.
</p>
<h2>Swoosh, swoop</h2>
<a
href="http://svn.openlaszlo.org/labs/newsmatch/progression07/main07.lzx">(source)</a>.
(live) [TODO linkme!]
<p>
- It's time for some swooshing. The seventh iteration (I skipped six) adds a
floating image
+ It's time for some swooshing. The seventh iteration (We're skipping the
sixth, but it's in the repository) adds a floating image
that "slides" the selected thumbnail down to the gDetails view. When it
arrives, the
description in the details view is updated. We manage this with an
animator (note how two
animators are run simultaneously for an interesting visual effect), a
partially-transparent
@@ -238,7 +236,7 @@
</animatorgroup>
</view>
</pre>
-<p>Also in this iteration, I defer the loading of the thumbnails until after
the opening animation finishes.
+<p>Also in this iteration, the loading of the thumbnails is defered until
after the opening animation has finished.
The <tt>rssimage</tt> class has an empty view until <tt>loadThumbnail</tt>
is called on it; only then
does the app start loading the image resource:
<pre>
@@ -254,12 +252,12 @@
</class>
</pre>
- This way, the application will start to appear while the images are still
loading; just another tweak
+ This way, the application starts to appear while the images are still
loading. This is another tweak
to improve the user experience. On a slow connection, you can see the
images pop in one by one.
</p>
<h2>Application Logic</h2>
<p>
- Didn't I say something about a matching game? Yep. The <a
href="http://svn.openlaszlo.org/labs/newsmatch/newsmatch/main.lzx" title="final
code">final code</a> shows how I did this, by taking advantage of the guid
(global unique identifier) that the rss feed provides for each item. I bind the
guid to both the <tt>rssimage</tt> and the <tt>rsslabel</tt>. To see if a title
matches an image, I just compare their guid's.
+ Didn't we say something about a matching game? Yep. The <a
href="http://svn.openlaszlo.org/labs/newsmatch/newsmatch/main.lzx" title="final
code">final code</a> shows how we did this, by taking advantage of the guid
(global unique identifier) that the rss feed provides for each item. We bind
the guid to both the <tt>rssimage</tt> and the <tt>rsslabel</tt>. To see if a
title matches an image, we just compare their guid's.
</p>
<pre>
<view id="gDetails" ... >
@@ -280,11 +278,12 @@
</pre>
<h2>Birds in Flight</h2>
<p>EDGE data transfer can be slower than a laden African swallow, so you have
to be very careful with your bloated feeds.
- The rss feed served up by yahoo is much bigger than I need for this
application, and it has lots of duplicate information; it started out at around
150k. I'm grooving on XSLT these days, so I made a <a
href="http://svn.openlaszlo.org/labs/newsmatch/filter.xsl" title="xslt
filter">little xslt filter</a> that pulls out just the information I need. This
got the feed info down to around 11k. If I was deploying newsmatch live, I'd
insert this filter into the data flow from the rss provider to newsmatch; as
is, I just run it by hand offline when I want some new data.
+ The rss feed served up by yahoo is much bigger than we needed for this
application, and it has lots of duplicate information; it started out at around
150k. So, we made a <a
href="http://svn.openlaszlo.org/labs/newsmatch/filter.xsl" title="xslt
filter">little xslt filter</a> that pulls out just the information needed for
NEWSMATCH. This got the feed info down to around 11k. If we were deploying
NEWSMATCH live, we would insert this filter into the data flow from the rss
provider to newsmatch; as is, we just run it by hand offline when we want some
new data.
</p>
<h2>Custom Wrappers</h2>
-<p>If you run <a href="http://labs.openlaszlo.org/ipdc/awip03/"
target="newsmatch">the final version of newsmatch</a> you'll notice that the
words NEWS/MATCH appear on the screen pretty fast, even on the iPhone over
EDGE, and that the "tap to begin" instruction doesn't appear for a while. This
is more sleight-of-hand. An OpenLaszlo application, even a DHTML application,
runs inside a standard html page generated by the OL server. This "embed" page
does some browser-sniffing and loads in a "loading" splash, which is replaced
by the actual OL application when it is ready. (See the <a
href="http://www.openlaszlo.org/lps4/docs/developers/browser-integration.html"
title="Chapter 35. Browser Integration">OpenLaszlo Developer's Guide
on Browser Integration</a>.)
+<p>If you run <a href="http://labs.openlaszlo.org/ipdc/awip03/"
target="newsmatch">the final version of newsmatch</a> you'll notice that the
words NEWS/MATCH appear on the screen pretty fast, even on the iPhone over
EDGE, and that the "tap to begin" instruction doesn't appear for a while. This
is more sleight-of-hand. An OpenLaszlo application, even a DHTML application,
runs inside a standard html page generated by the OpenLaszlo server. This
"embed" page does some browser-sniffing and loads in a "loading" splash, which
is replaced by the actual OL application when it is ready. (See the <a
href="http://www.openlaszlo.org/lps4/docs/developers/browser-integration.html"
title="Chapter 35. Browser Integration">OpenLaszlo Developer's Guide
on Browser Integration</a>.)
</p>
+<!-- you so clever, you guys! -->
<p>
To do this trick, you have to do a SOLO deployment. (Please see the <a
href="http://www.openlaszlo.org/lps4/docs/developers/proxied.html"
title="Chapter 25. Proxied and SOLO Applications">OpenLaszlo
Developer's Guide</a> for a description of SOLO deployment.) For this
application, we edited the html wrapper so that the wrapper's splash exactly
matches the lzx startup screen. This was just plain old HTML work; we found the
div "lzsplash" in the wrapper page, and replaced the entire lzsplash div with
this:
<pre>
_______________________________________________
Laszlo-checkins mailing list
[email protected]
http://www.openlaszlo.org/mailman/listinfo/laszlo-checkins