Real data, talking to sqLite
Most modern browsers supports some form of local database engine. For webkit and mozilla based browser that local database is sqlite, which (behind the scene) is built into the actual browser program. Internet explorer does not support this api but have other mechanisms for local data manipulation, including the ability to store data as XML.
In short, there are several database standards, but the most known are: IndexDB and Web SQL database. Before we dig into the nitty gritty of these things, let’s familiarize ourselves with the landscape.
Security and size restrictions
Browser vendors are constantly challenged when it comes to security. Hackers have proven that even the slightest weakness in security can be exploited, often with costly consequences for the unsuspecting user. Since this is the case you are not allowed to simply access or create a database anywhere you want. When you create a database the file is kept in a secure section of the browser cache and is explicitly linked to your web applications url.
There is also the case of database size, which in most browsers is set to between 5 and 10 megabyte by default. If the user needs more data he or she will have to manually change this. While 5 megabytes sounds ridicules to a delphi programmer, who is used to handle gigabytes of data in a plethora of different database engines – the browser database api was not created to be your central database solution. It was created to allow you to cache data between your web-apps and your server.
Removing the restrictions using phonegap
The web apps you create with Smart Mobile can be wrapped with a system called phonegap. Phonegap takes you generated html, javascript and css files (and any other files connected to your html) and injects it into a real .exe file for both Android and iOS. This real .exe file creates it’s own browser instance, puts the browser in full-screen – and disables the security restrictions that public browsers imposes on you. Once wrapped, you can create persistent (non cached) databases of any size. Or at least without the 5 to 10 megabyte limitation.
Using a central server
In our typical scenario we have a RPC server which supports JSON (the format browsers use to send data, a bit like XML only more compact) messages. We like to use remobjects remoting framework, but you should be able to use the standard delphi webservice components to achieve the same thing – or indeed the excellent delphi on rails toolkit by Henri Gourvest.
So on our server we would expose an interface that external client apps (web or native) can call to extract data. Below is a hypothetical variant of such an interface. Note: this is just an example, there are must better ways of handling remote databases, but we have to start somewhere.
type IMyDataService = interface function GetTopId: Int64; function GetCountBetween: Int64; function ReadRowsFromTop(const aCount: Integer):String; function ReadRowsFromOffset(const aRecId: Int64):String; procedure UpdateRows(const aRowData: String; const aAppend: Boolean); end;
This would be the bare minimum you could use to mirror a small news-feed between a client and a mobile app. The functions would return the database rows in JSon format. Now lets go through a possible chain of communication between the server and your mobile app.
When you mobile application starts, it will call getTopId() to get the current topmost record-id from the database server. It will then compare the number it get back to it’s own “last known id”. If they are different, you know more news have been added on the server and that your local database needs to be updated.
If you want to know the exact number of records that have been added since your last update, you can call getCountBetween(), sending in the record-id of your last record. The server would return the number of rows added to the database since that number was “first”.
Based on this information, your web app would check that the amount of new records dont exceed the storage capacity of the database and ultimately call either readRowsFromTop() or readRowsFromOffset() if you want a spesific data range.
Again I must stress that this is not a very efficient way of doing things, it serves as an example of how you could sync a client with a server, nothing more.
IndexDB
Note: As of writing Smart 1.0 has just been released. It provides the foundation that we are going to build on, but sadly there was not enough time to add pre-made database components to this initial release. We have already started working on a TClientDataset class that will serve as the middle-man between cached updates from a server – and your local sqlite engine inside the browser. But as of writing the API is not ready.
In order to create a database we have to resort to some assembly code, which means javascript in our case. Creating a local database is quite easy. But handling javascript’s async nature in a linear way is not easy.
function w3_getIndexDB:THandle; Begin result:=null; asm if (window.indexedDB) return window.indexedDB; if (window.webkitIndexedDB) return window.webkitIndexedDB; if (window.mozIndexedDB) return window.mozIndexedDB; if (window.msIndexedDB) return window.msIndexedDB; end; end; // Returns the request object function w3_openIndexDB(const adbName:String):THandle; var mEngine: THandle; Begin mEngine:=getIndexDB; if (mEngine) then Begin result:=mEngine.open(adbName); end else result:=null; end;
Now comes the tricky part, because we are really dealing with an objective database and not a sequential database. This means that each record is associated with a key and a datastore (which functions as a table). Since javascript is async, every operation we perform needs more infrastructure as well. So here is how you could open up a database and handle events:
var myDb:THandle; begin myDB:=w3_openIndexDB('mydb'); if (myDB) then begin myDb.onerror:=procedure (event:variant) begin //catch errors here end; myDB.onsuccess:=procedure (event:variant) begin //handle success codes here end; myDB.onupgradeneeded:=procedure (event:variant) begin //alter schema here, create tables (datastores) and setup key generators end; end; end;
More to come soon! We are currently busy turning these things into proper objects so you don’t have to deal with this low-level stuff
Any news on this article?? I am very interested in testing database linked app
Hi Donovan
Yes it’s coming. The database units are our #1 priority next to finishing version 1.0. So the database units will be a download available to our customers straight after the product is released. On our list is also wrapping the sound API and providing a full phonegap wrapper. But right now focus is on the final pieces of the IDE.
For Client-Server communication, do not forget about http://mormot.net
This is now a well established Client-Server ORM and SOA framework, including a strong security model, best speed possible, and interface-based services.
We are currently prototyping direct client access from Smart, using plain Smart Object Pascal code, including authentication and strong security.
See http://blog.synopse.info/post/2012/04/19/Smart%3A-mORMot%2C-from-Delphi-to-JavaScript about how we managed to code CRC32 and SHA256 in a Smart compatible way (this is more technical than with plain Delphi/FPC).
It is full Open Source, working from Delphi 6 up to XE2, with a growing base of users.
BTW, I’m not sure the MVC model of DoR is directly applicable to Smart clients – of course, the RESTful part is, but DoR interrest is not just JSON SuperObject over HTTP.
I suspect you want to add more and more documentation to your project. Take a look at our open source tool SynProject: mORMot’s documentation is created with it. It is easy to use (at least for programmers), and powerfull to create technical docs, including source code, indexs, and links. See http://synopse.info/fossil/wiki?name=SynProject
I suspect that if you want your code to be PhoneGap ready, you would better add “window.openDatabase” to your w3_getIndexDB function. Or is PhoneGap mapping the window.indexedDB instance?
That will be handled by the driver. Things like checking for database support, gyroscope – all these things is up to the driver to handle. It is created when you app starts up after checking what browser you are running and it’s caps. So a phonegap driver is all that would be needed (and it would just override the webkit driver) to fill the phone gap so to speak 🙂
It seems it had been almost 1 years since this article published. Do you have any news for that? We would like to have a database front end that can work offline in tablets / mobiles.
I already tested the connection to Remobjects SDK so more concert on the local database storage and accessibility. A TQuery like objects to handle local tables would be a great help. A TClientDataset class mentioned above is a great addition to make data sycn more easy.
So we would like to know the status of the database handling of SMS before we can decide to go for this options for our new front-end. Thank you very much.
We have implemented webSQL (w3websql.pas) which provide the basic classes for using the browsers built in database. We are currently in the pipeline of porting DataSnap. Sadly we cant give an estimate for when that aspect will be ready. But using the websql classes is fairly straight forward – but field to control binding is still some time into the future.
Thanks for your prompt answer. I had test the websql sample which work in android and chrome but not IE and FF (even the plug-in failed to work after FF17). The more important point is that it had stopped development in Nov 2010 and so have no future.
http://en.wikipedia.org/wiki/Web_SQL_Database
At least it can be used in both tablet/mobile environment and desktop too for now.
I am also wondering what is the most efficient way to transfer a dataset result from remobjects server to SMS client.
A Delphi dataset like objects will be very useful in both case.