De iPhone is een van de populairste mobiele telefoons die er op dit moment te koop zijn. En het is niet alleen een prima telefoon, hij is ook prima als mini computer te gebruiken. Dat is nauwelijks nieuws te noemen gezien de hoeveel gebruikers en ontwikkelaars die al actief zijn in de iPhone AppStore. Nu hebben .NET ontwikkelaars alleen een nadeel, je kan niet zomaar met .NET een applicatie voor de iPhone schrijven. Er zijn wel mogelijkheden met MonoSharp maar dan kan men nauwelijks gebruik meer maken van Visual Studio en is men op een Apple aangewezen.
Betekent dit dat er voor .NET ontwikkelaars niets anders op zit dan naar een Apple over te schakelen? Nee, gelukkig niet! Op de iPhone draait namelijk een zeer krachtige browser met uitgebreide ondersteuning voor HTML 5 onderdelen. Dit maakt veel mogelijk wat met traditionele web applicaties niet mogelijk is zoals een locale database en offline ondersteuning. Daarbij kan in JavaScript ontwikkeld worden, iets waar Visual Studio prima ondersteuning voor heeft.
We kunnen uiteraard met een lege HTML pagina beginnen maar zoals zo vaak zijn er al applicatie frameworks beschikbaar die een deel van de problemen voor ons oplossen. In dit geval is dat jQTouch, een framework dat boven op jQuery is gebouwd en al met ASP.NET MVC meegeleverd wordt. Door gebruik te maken van jQTouch is het gemakkelijk een iPhone web applicatie te maken die heel natuurlijk aanvoelt voor iPhone gebruikers. Zo worden er bijvoorbeeld verschillende animaties ondersteund om van pagina’s te wisselen maar ook de GPS wordt ondersteund om locatie afhankelijke applicaties te maken.
Beginnen met jQTouch
Om te beginnen met jQTouch moeten we eerst de library downloaden. Deze is op http://www.jqtouch.com te vinden. In de zip file zitten naast de JavaScript en CSS code een aantal nuttige voorbeelden om mee te beginnen. Naast de code zijn er ook twee verschillende themes meegeleverd. De eerste is een Apple theme met de standaard iPhone kleuren en een tweede is een jQTouch theme met een stijlvolle zwarte kleur.
Image 1: Het jQTouch theme in een iPhone simulator.
Ik ga gebruik maken van een ASP.NET MCV applicatie voor extra functionaliteit in een volgende demo. Voor het begin gaan we een standaard HTML pagina gebruiken binnen het project. Nadat we het ASP.NET MVC project aangemaakt hebben moet we eerst de benodigde jQTouch files toevoegen. De jQTouch.js komt in de Scripts directory en de jQTouch.css file komt in de Content folder. Daarnaast hebben we één van de themes nodig uit de jQTouch themes folder. In dit geval gebruik ik de inhoud van de jqt theme folder en kopiëer deze ook naar de Content folder.
De eerste pagina wordt een standaard HTML pagina. In deze lege pagina hebben we eerst de basisbenodigdheden nodig. Dit zijn verwijzingen naar de jquery-1.3.2.js en jqtouch.js script files en de theme.css en jqtouch.css style sheets. Nadat we deze verwijzingen hebben is de volgende stap een jQTouch object te maken.
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "
http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="
http://www.w3.org/1999/xhtml">
<head>
<script src="Scripts/jquery-1.3.2.js" type="text/javascript"></script>
<script src="Scripts/jqtouch.js" type="text/javascript"></script>
<link href="Content/theme.css" rel="stylesheet" type="text/css" />
<link href="Content/jqtouch.css" rel="stylesheet" type="text/css" />
<script type="text/javascript">
var jQT = new $.jQTouch();
</script>
<title></title>
</head>
<body>
</body>
</html>
Listing 1: < De basis pagina met jQTouch >
Hoe kunnen we onze applicatie testen?
Het beste is het uiteraard om de applicatie te testen op een echte iPhone. In de praktijk is het niet echt handig om dat constant te doen. Tijdens het ontwikkelen is een simulator handiger. Er is binnen de Apple iPhone SDK een goede iPhone simulator. Helaas werkt deze simulator alleen op een Mac en niet op een Windows PC. Bij het ontwikkelen op een PC gebruik ik de desktop Safari browser van Apple. Deze lijkt heel sterk op de mobiele versie en werkt prima. Om snel te kunnen zien hoe de applicatie er op een iPhone uitziet gebruik ik TestiPhone.com, een online iPhone simulator die we in Safari laden. Binnen TestiPhone.com geven we dan de URL van de te testen applicatie op, dat mag een URL op de machine zelf zijn.
De structuur van een jQTouch applicatie
Binnen een jQTouch applicatie kunnen we verschillende “pagina’s” hebben. In tegenstelling tot een normale web applicatie zijn dit echter geen aparte pagina’s met eigen URL’s maar verschillende div elementen in een hoofdpagina waarvan er één zichtbaar gemaakt wordt. Dit laatste doet jQTouch automatisch voor ons.
De toolbar die bij een iPhone applicatie meestal bovenaan getoond wordt bestaat uit een geneste div, in dit geval met de css class “toolbar”. Hierbinnen kan eventueel een h1 element gebruikt worden om een titel te tonen. Er zijn verschillende manieren om een andere “pagina” te activeren binnen jQTouch. De meeste mensen beginnen met een lijst met links naar een vervolg pagina. Dit wordt standaard gemaakt door een unordered list met links naar de verschillende pagina’s te maken. jQTouch zal de navigatie actie bij een klik onderscheppen en de gewenste pagina zichtbaar maken.
<body>
<div id="MainPage">
<div class="toolbar">
<h1>
Main</h1>
</div>
<p>
De eerste pagina</p>
<ul>
<li><a href="#Page1">Pagina 1</a></li>
<li><a href="#Page2">Pagina 2</a></li>
</ul>
</div>
<div id="Page1">
<div class="toolbar">
<a class="back" href="#">Terug</a>
<h1>
Een</h1>
</div>
<p>De eerste pagina</p>
<ul>
<li><a href="#Page2">Pagina 2</a></li>
</ul>
</div>
<div id="Page2">
<div class="toolbar">
<a class="back" href="#">Terug</a>
<h1>
Twee</h1>
</div>
<p>De tweede pagina</p>
<ul>
<li><a href="#Page1">Pagina 1</a></li>
</ul>
</div>
</body>
Listing 2: < De HTML body van de eerste pagina >
Dit alles gebeurt binnen één pagina zonder dat er verbinding gemaakt wordt met de server. Het standaard gedrag van jQTouch is om de clicks van een anchor binnen een listitem in een unordered list op de body te gebruiken als links. Dit is aan te passen door gebruik te maken van de initialisatie opties, iets wat we in dit voorbeeld niet gebruiken. Nu we het toch over de initialisatie opties hebben. Met deze opties zijn er tal van aanpassingen te doen hoe jQTouch werkt en een applicatie er uit ziet. Een paar interessante zijn: het gebruik van een startup screen, een icon voor op de iPhone achtergrond of het preloaden van plaatjes die later in de applicatie gebruikt worden. Zie http://code.google.com/p/jqtouch/wiki/InitOptions voor een complete lijst van alle opties.
Een eenvoudige applicatie maken
Met deze basiskennis van jQTouch is het niet moeilijk om met behulp van ASP.NET MVP een applicatie te maken die er op de iPhone heel normaal uitziet. De applicatie die ik als demo gebruik laat zien wat de verschillende .NET gebruikersgroepen in Nederland aan bijeenkomsten organiseren. De structuur is eenvoudig, de gebruiker start de applicatie en ziet een lijst van de mogelijke gebruikersgroepen. Zodra de gebruiker op een groep klikt verschijnt er een lijstje met bijeenkomsten. Door vervolgens op een bijeenkomst te klikken krijgt de gebruiker een pagina met informatie over die bijeenkomst te zien.
Als de gebruiker deze eerste HtmlDemo start wordt van de HtmlDemoController klasse de Index functie uitgevoerd. Deze haalt de benodigde data op en geeft een ViewResult terug die de Index.aspx uit de Views\HtmlDemo folder weergeeft. De code voor de controler ziet er als volgt uit:
public class HtmlDemoController : Controller
{
public ActionResult Index()
{
var meetings = Meeting.Load();
var groups = UserGroup.Load();
var query = from ug in groups
select new UserGroupViewModel
{
Group = ug,
Meetings = meetings.Where(
m => m.UserGroupId == ug.Id ||
m.UserGroupId == null).ToList()
};
ViewData["UserGroups"] = query.ToList();
ViewData["Meetings"] = meetings;
return View();
}
}
Listing 3: < MVC controler action >
De view zelf bevat de code die op basis van deze data de benodigde HTML gaat produceren. De header van de view is eigenlijk hetzelfde als in het bovenstaande voorbeeld. Eerst worden de benodigde stylesheets en scripts geladen. Daarna wordt het jQTouch object aangemaakt. Net als in het eerste voorbeeld willen we ook nu in de body alle benodigde data hebben op het moment dat de pagina naar de browser gaat. Die HTML wordt nu op basis van de bovenstaande data gegenereerd. Als eerste hebben we de lijst van gebruikersgroepen nodig voor het hoofdmenu. Dit wordt met het volgende stuk code gedaan:
<%-- Write usergroup main menu --%>
<div>
<div class="toolbar">
<a class="back" target="_webapp" href="/CodeCamp">Home</a>
<h1>
CodeCamp</h1>
</div>
<ul>
<% foreach (var group in (IEnumerable<UserGroupViewModel>)ViewData["UserGroups"])
{
UserGroup ug = group.Group;
%>
<li><a href="#<%= Html.Encode(ug.Id) %>">
<%= Html.Encode(ug.Name) %></a></li>
<% } %>
</ul>
</div>
Listing 4: < hoofdmenu met de gebruikersgroepen >
Vervolgens hebben we voor elke gebruikersgroep een lijst van benodigde bijeenkomsten nodig. Dit gebeurt met de volgende code:
<%-- Write out each usergroup and its meetings --%>
<% foreach (var group in (IEnumerable<UserGroupViewModel>)ViewData["UserGroups"])
{
var ug = group.Group;
var meetings = group.Meetings;
%>
<div id="<%= Html.Encode(ug.Id) %>">
<div class="toolbar">
<a class="back" href="#">Terug</a>
<h1>
<%= Html.Encode(ug.Name) %></h1>
</div>
<div class="logo">
<img src="<%= Html.Encode(ug.Logo) %>" />
</div>
<ul>
<% foreach (var meeting in meetings)
{
%>
<li><a href="#<%= Html.Encode(meeting.Id) %>">
<%= Html.Encode(meeting.Title) %></a></li>
<% } %>
</ul>
</div>
<% } %>
Listing 5: <De bijeenkomsten per gebruikersgroep>
Als laatste hebben we per bijeenkomst een pagina nodig met de details voor die bijeenkomst. Dit wordt met de volgende code gegenereerd:
<%-- Write out all meetings --%>
<% foreach (var meeting in (IEnumerable<Meeting>)ViewData["Meetings"])
{
%>
<div id="<%= Html.Encode(meeting.Id) %>">
<div class="toolbar">
<a class="back" href="#">Terug</a>
<h1>
<%= Html.Encode(meeting.Title) %></h1>
</div>
<div style="margin: 25px;" class="description">
<%= meeting.Description %>
</div>
</div>
<% } %>
Listing 6: <Een pagina per bijeenkomst>
Alles bij elkaar is het genereren van de pagina niet erg moeilijk en hebben we al snel een bruikbare applicatie.
Het is dan misschien niet moeilijk de applicatie op deze manier te maken maar er kleven wel een paar nadelen aan die hier misschien niet gelijk duidelijk worden. De applicatie laadt hier namelijk maar heel weinig data zodat de uiteindelijke pagina niet erg groot wordt. Maar bij een applicatie die veel data laadt wordt de HTML pagina al snel veel te groot en moet de gebruiker lang wachten voor er iets gebeurt. Het mag dan ook duidelijk zijn dat deze aanpak niet werkt voor een webwinkel met enkele duizenden artikel in het assortiment.

Image 2: Het hoofdmenu van onze applicatie.
Werken met grotere hoeveelheden data
Zodra we met grotere hoeveelheden data gaan werken is het van te voren genereren van alle HTML geen optie meer. We kunnen dan op verschillende manieren te werk gaan om een applicatie te maken die wel goed werkt. Waar de meeste ontwikkelaars het eerst aan zullen denken is met AJAX aan de slag gaan. Dit is een prima optie aangezien we zowel op de server gegenereerde HTML kunnen gebruiken als met jQuery JSON data ophalen en binnen de browser HTML genereren. In de voorbeeld code is deze aanpak te zien in de AjaxDemoController en de bijbehorende view.
Er is echter een andere optie die nog meer voordelen oplevert en dat is de data op te halen en in een database binnen de browser te bewaren. Deze data blijft bewaard, ook als de browser afgesloten is, en kan volgende keer direct gebruikt worden als de applicatie opgestart wordt. Dit is mogelijk omdat de browser op de iPhone al een groot deel van de HTML 5 specificaties implementeert waar lokale database opslag er één van is. Dit wordt nog interessanter als we deze HTML 5 eigenschap combineren met die van offline applicaties. Die twee samen zorgen er voor dat we een volledig functionele database applicatie kunnen schrijven die lokaal bewaard wordt en offline op de iPhone bruikbaar is. Dat allemaal zonder de verplichting om door de Apple iPhone AppStore te gaan.
Sidebar: Voor en nadelen van de Apple AppStore
Er zitten zowel voor- als nadelen aan het verspreiden van een applicatie via de Apple AppStore. Het grote voordeel is dat er miljoenen mensen, en veel software review sites, gebruik maken van de AppStore en dat de applicatie zo gemakkelijk gevonden kan worden. Koppel daar het gemak van het betalen van een Euro aan en een ontwikkelaar kan een leuk bedrag aan een populaire applicatie verdienen. De AppStore heeft echter ook een keerzijde. Er komen namelijk alleen applicaties in die door Apple goedgekeurd worden. En er worden nogal eens applicaties om twijfelachtige redenen afgekeurd. Los daarvan kost het review proces tijd waardoor bug fixes langer dan nodig op zich laten wachten. Verder is de AppStore een publiek geheel en dus niet de plaats om interne bedrijfs specifieke applicaties te plaatsen. Om niet via de Apple AppStore te hoeven werken is het maken van een HTML5/JavaScript applicatie die offline genomen kan worden een goed alternatief. Overigens sluiten we een gang naar de AppStore niet uit aangezien hulpmiddelen als PhoneGap, www.phonegap.com, een jQTouch applicatie kunnen inpakken en via de AppStore kunnen verspreiden.
Sidebar: Iets meer over de iPhone browser
De browser op de iPhone is de mobiele variant van de Safari browser die ook op de desktop te krijgen is. Deze browser ondersteunt een flink deel van de HTML5 specificaties waaronder die voor locale databases en offline applicaties. Omdat deze functionaliteit zowel op de desktop als op de iPhone beschikbaar is kan een applicatie prima in de desktop Safari browser getest worden. Voor het database deel heeft Apple besloten om SQLite te gebruiken. De SQLite database is op veel platformen te gebruiken en de documentatie over het gebruik is op www.sqlite.org te vinden. De manier waarop een database in de browser vanuit JavaScript benaderd wordt is op www.w3.org/TR/webdatabase te vinden. Voor het offline nemen van HTML 5 applicaties is op http://www.whatwg.org/specs/web-apps/current-work/#offline meer te vinden.
De database versie van de iPhone applicatie
Laten we weer beginnen bij het ASP.NET MVC controler deel van de applicatie. Die ziet er nu iets anders uit aangezien de data nu los van de HTML naar de browser gestuurd wordt. Deze code ziet er als volgt uit:
public class DatabaseController : Controller
{
public ActionResult Index()
{
return View();
}
public JsonResult GetUserGroups()
{
var result = UserGroup.Load().
OrderBy(g => g.Name).ToList();
return Json(result);
}
public JsonResult GetMeetings()
{
var result = Meeting.Load().
OrderBy(m => m.Description).
ToList();
return Json(result);
}
}
Listing 7: <De MVC Controler voor de database iPhone applicatie.>
De Index functie doet nu niets meer dan de view teruggeven, deze wordt hieronder besproken. De GetUserGroups() en GetMeetings() functies halen nu de data op en geven dit terug als JSON data zodat die gemakkelijk te verwerken is in JavaScript.
Het HTML deel van de view is nu ook veel eenvoudiger. Het is niet veel meer dan een lege schil, maar wel volgens de eerste structuur, waar de JavaScript code data aan toe gaat voegen.
<body>
<%--Main menu, display a list of user groups--%>
<div>
<div class="toolbar">
<a class="back" target="_webapp" href="/CodeCamp">
Home</a>
<h1>
CodeCamp</h1>
</div>
<ul id="groupsList">
</ul>
</div>
<%--Display a single user group with a list of meetings--%>
<div id="singleGroup">
<div class="toolbar">
<a class="back" href="#">Terug</a>
<h1>
</h1>
</div>
<div class="logo">
</div>
<ul id="meetingList">
</ul>
</div>
<%--Display a single meeting--%>
<div id="meeting">
<div class="toolbar">
<a class="back" href="#">Terug</a>
<h1>
</h1>
</div>
<div style="margin: 25px;" class="description">
</div>
</div>
</body>
Listing 8: <De HTML body voor de database iPhone applicatie>
De meeste logica van de applicatie zit nu in de JavaScript code die in de browser draait. Deze code is wel wat ingewikkelder geworden maar valt eigenlijk wel mee. Voor de meeste .NET programmeurs is het voornamelijk even wennen aan de asynchrone manier van werken binnen JavaScript maar zodra dat een beetje gewend is blijkt het best gemakkelijk te werken.
De eerste keer dat de gebruiker de applicatie opstart zullen we de database moeten aanmaken. Hier valt het gelijk op hoe gemakkelijk het werken met SQLite is, er hoeven namelijk geen data types gebruikt te worden, iets wat in een omgeving als JavaScript heel natuurlijk is.
// Open and create the database as required.
function setupDatabase() {
db = openDatabase("CodeCamp", "1.0", "CodeCamp", 65536, function(db) { alert(db); });
if (db != undefined) {
db.transaction(createTables, errorHandler);
}
}
// Create the database tables if needed.
function createTables(tx) {
tx.executeSql('create table if not exists Groups (Id, Name, Logo);');
tx.executeSql('create table if not exists Meetings (Id, Title, Description, UserGroupId);');
}
Listing 9: <Het openen van de database en aanmaken van de tabellen>
Naast het ontbreken van data types valt hier gelijk te zien hoe er binnen HTML4/JavaScript met een database gewerkt moet worden. Eerst wordt er een database object aangemaakt via de openDatabase() functie. Als er vervolgens iets met deze database gedaan moet worden is het steeds eerst een transactie starten met de transaction() functie en daar een functie aan meegeven. Deze functie wordt dan asynchroon aangeroepen en krijgt het transactie object als eerste parameter. Met dit transactie object kan dan de executeSql() functie aangeroepen worden om iets te doen, in dit geval de benodigde tabellen aanmaken.
Nadat de database open is en de tabellen aangemaakt zijn kunnen we met de jQuery getJSON() functie de data van de server opvragen. In dit geval vragen we eerst de gebruikersgroepen op en geven we de getUserGroupsCompleted() functie mee om uit te voeren als de data geladen is. In de code is goed te zien hoe we dat weer binnen een transactie met de executeSql() functie doen en dat we binnen SQLite gewoon parameters kunnen gebruiken bij een SQL commando.
// The user groups have been loaded.
// Store them in the local database.
function getUserGroupsCompleted(groups) {
$.getJSON("Database.aspx/GetMeetings", null, getMeetingsCompleted);
db.transaction(function(tx) {
tx.executeSql("DELETE FROM Groups");
$(groups).each(function(i, item) {
tx.executeSql("INSERT INTO Groups
(Id, Name, Logo) VALUES (?,?,?)",
[item.Id, item.Name, item.Logo]);
});
}, errorHandler, displayUserGroups);
}
Listing 10:< De functie om de opgehaalde gebruiksgroepen te bewaren>
Na het opvragen van de gebruikersgroepen vragen we een lijst van bijeenkomsten op die we op eenzelfde manier in de database bewaren.
Het in de browser tonen van de gegevens is nu ook best gemakkelijk geworden. In de displayUserGroups() functie hieronder worden de gegevens uit de database geladen en in de unordered list gezet.
// Display the usergroups in the local database.
function displayUserGroups() {
var ul = $("#groupsList");
ul.empty();
db.transaction(function(tx) {
tx.executeSql("Select * from Groups",
null, function(tx, result) {
for (var i = 0; i < result.rows.length; i++) {
var item = result.rows.item(i);
ul.append("<li class='arrow'>" +
"<a href='#singleGroup' groupId='" +
item.Id + "'>" +
item.Name + "</a></li>");
}
});
}, errorHandler);
}
Listing 11: <Het zichtbaar maken van de gebruikersgroepen>
Uiteraard blijven deze gegevens in de database zodat we bij de volgende keer dat de applicatie gestart wordt niet hoeven te wachten tot alles geladen is voor we de gebruiker wat laten zien. Iets wat de hele gebruikers ervaring veel beter maakt. Daarnaast opent dit ook de deur naar het offline nemen van de applicatie door een manifest op te nemen waarin staat welke bestanden de browser moet lokaal moet bewaren. Het offline nemen van de applicatie gaat echter buiten de bereik van dit artikel.
Conclusie
jQTouch is een zeer interessante JavaScript bibliotheek voor mensen die applicaties voor de iPhone willen maken maar dat niet in Objective-C op een Apple willen doen. Het werken met het officiële platform heeft altijd voordelen doordat er een hoop mogelijk is maar geeft, vooral door de AppStore en het hieraan gekoppelde goedkeuring systeem, ook eigenlijk wel de nodige beperkingen. Door gebruik te maken van jQTouch is de stap naar de iPhone veel minder groot. En als we een met jQTouch ontwikkelde applicatie later toch in de AppStore willen zetten kan dat altijd nog met behulp van oplossingen als PhoneGap die een JavaScript oplossing verpakken tot een normale applicatie.