I found out that using JSON instead of XML can be up to 30 times faster for the use-cases from my last article: reading/writing 10’000 addresses or reading/writing 119 speakers. For applications where you get data from REST or WebServices, cache them local and use them in-memory it’s very common to read/write to your file-based cache. My Conference Apps are going this way: caching and queuing, work in-memory with GroupDataModel. Next articles will explain HowTo Search and Filter.

From discussions in forum I was asked to compare the speed if using a SQLite Database on your BB10 Device.

I did this and enhanced my sample app to compare the execution times also using SqlDataAccess.

JSON vs SQL in the UI

In this article I won’t discuss the QML UI, because I was using same patterns as already described for JSON vs XML. Here are the screenshots.

Reading/Writing 10’000 Addresses I added two Bars for SQL:

From the values you can see that reading 10’000 addresses using SQL is even faster then JSON:

READ JSON: 2632 ms

READ XML: 79795 ms

READ SQL: 1496 ms

Same for writing: SQL is the fastest way to do it

WRITE JSON: 5587 ms

WRITE XML: 3787 ms

WRITE SQL: 1788 ms

Cascades does a really great job not only for JSON but also using a local SQLite DB.

For the 119 Speakers I added an ActionItem to push the SpeakerSQLPage.qml comparing execution times of JSON and SQL:

Now the results are different: Reading from JSON or from SQLite is similar. Doing some executions in series sometimes JSON is faster, sometimes SQL is faster.

Writing the 119 Speakers into the DB took around three times longer as writing them into a JSON file. Seems there’s some initial overhead using a SQL DB, so inserting less records can be slower then JSON. If you’re doing the write async, the user won’t notice this.

Rule #1: if caching data, use JSON or SQL – both are much faster then XML

If you take a look at the file sizes needed, the JSON or XML files storing the 10’000 addresses occupy 4 MB from your disk space where the SQLite only needs 1.7 MB

Rule #2: if caching a lot of data: SQL uses disk space more efficient then JSON or XML

Read all data from SQLite DB

The result from the Query is a QVariantList containing QVariantMap for each Address.

So this is similar to JsonDataAccess.

Write (INSERT) all data into SQLiteDB

Writing all data to a SQLite DB means you have to INSERT from a QVariantList containing QVariantMaps for all your addresses / speakers / … INTO the DB.

Inserting all data into the DB at first you have to think about replacing current data. Writing to a JSON file the content was automatically overwritten. If you want to replace all data of a Table you have to DROP an existing Table:

mSQLda->execute("DROP TABLE IF EXISTS " + ADR_TABLE_NAME);

As next step you have to CREATE the TABLE before you can INSERT any data. If you know the structure of your data you can use a hard-coded QString to do this. But we’re talking here about data getting from REST services in JSON or XML format. If you own the REST service and control the API you also can do it the hard-coded way. If you don’t know exactly what you’re getting, it’s better to dynamically create the Table.

If you know that all data you receive have the same properties, you can simply get the QVariantMap from first row:

ATTENTION: This only works if all QVariantMaps match exactly the properties from your parameterized statement. This will work perfect for the Addresses, but will fail for the Speakers !

The Speakers JSON is optimized and only contains properties with values. This took the smallest amount of space and memory. Getting those properties from a QVariantMap is no problem, because you can use defaults if the key wasn’t contained.

If you want to store all possible properties at first you have to make all QVariantMaps equal. There’s a helper method in the sample app to do this: firstInitSqliteSpeakers().

Please also have in mind that we’re using simple Maps with one level of data, where it’s easy to create a Table for. If using more complex data you have to carefully think about your SQLite Tables. As we’ve seen: SQLite is really fast, but can cause some overhead.

There’s no easy answer what will be the best. As always: it depends … 😉

Stay tuned for my next articles where I’ll go deeper into the data models and caching strategies for my Conference Apps.

The answer isn’t easy and will cover some more articles. Let me start to explain…

BBJam Asia uses a JSON DataModel

I’m using JSON to persist the data of my conference apps and there are many reasons. Using REST interfaces you still get many data from responses in XML format and in most cases you have to consume such data and cannot change the backend.

The first thing I’m always doing is to convert those XML data into JSON data before going on.

I cannot tell you details about the BBJam Asia Conference API, so I generated some mockup data:

10’000 random addresses to have a large file to experiment with

119 public available speaker data to have some conference-relevant data

The addresses I’m using are generated by a free service from fakenamegenerator.com I can highly recommend this service to developers if you have to provide prototype apps with data or if you want to test using large data sets. They even generate different data for the Country you selected and also GeoCoordinates.

The speaker data is a wordpress export file from another conference APP and contains real data from public available sites.

Addresses and Speaker data is in XML format. I’ll show you HowTo convert these files into JSON and explain WHY you should do this.

Both datasets can be found here:

While developing and testing in many cases you have to take a look at those data and JSON is much easier to read as of XML.

Sometimes this is useful for mockup data where you simulate inserting objects into List DataModels.

In most cases JSON datafiles are smaller then XML:

10’000 addresses in XML: 6.7 MB

10’000 addresses in JSON: 5.9 MB

If the XML is using many long property names the file size can be reduced much more, but sometimes if the XML structure is very complicated it may happen that a JSON file is larger then XML. (Take a look at the Speaker data)

Perhaps you already know that Cascades supports JSON very well, per ex. if using QVariants from C++ in QML where QVariantMap was mapped automatically to JavaScript Objects (== JSON). Perhaps this deep integration of mapping is the reason for ….

Speed Speed Speed 🙂

Inside a conference app most times I’m reading data from cache and this is where JSON clearly wins over XML. Here are some values I measured using the 10’000 addresses:

READ JSON: 2’392 ms

READ XML: 71’342 ms

Rule #1: if caching data, always use JSON – this can be up to 30 times faster then XML

It’s curious: writing data to file JSON is something slower then XML. Here’s what I measured for the 10’000 addresses:

WRITE JSON: 5’255 ms

WRITE XML: 3’567 ms

The WRITE speed differences are marginal and you can do the WRITE async and work on while you’re probably waiting to get the data from READ.

In some cases you’re getting more data as you need. Take a look at the Speaker data exported from WordPress as RSS in XML format. Both file formats: XML and JSON aren’t easy to read and contain too much data in a complex structure. But it’s easy to workaround: as soon as you get the data from HTTP Response transform it and remove unneeded properties, which will give you even more speed. Here’s the measurement using the 119 Speakers:

READ XML: 378 ms

READ JSON: 76 ms

READ JSON v2: 23 ms

JSON v2 contains only the attributes you need in a less complex structure.

Rule #2: if getting complex data remove unused properties to get even more speed

It’s boring only to compare numbers, so let’s use Cascades to demonstrate the speed of JSON – based persistence or caching and see how easy it is to convert data between XML and JSON:

UI (QML)

The sample application is only using two QML files:

main.qml

MeasureSpeakerPage.qml

main.qml contains a NavigationPane displaying a Page to visualize the measurements of reading / writing the 10’000 addresses.

MeasureSpeakerPage.qml will be pushed on top to visualize the measurements of reading Speaker data from XML, JSON and JSON less properties.

Let’s take a look at the main.qml first. There are some attachedObjects:

We’re using the SystemToast as an easy way to let the user know if a process was started or finished.

While the first Page is defined directly inside the main.qml, the MeasureSpeakerPage will only be pushed on demand if the users starts an Action. To avoid unnecessary creation of UI Controls, we only define the component and create it dynamically before pushing on top.

The QTimer is used as a workaround to have a small delay between hitting the Action item and calling the process in C++. As we’ve seen above it takes a long time to read the 10’000 Addresses from XML: more then 1 Minute ! While executing this task we want to disable the Action items what was done async and needs some ms.

Without using the QTimer there will be not enough time to disable the ActionItems. Why ? Because we’re doing something you never never never should do in a real application: executing a long-running task on the UI Thread. While Network requests are async operations out of the box, reading a file isn’t and will block the UI.

The goal of this demo application is to let you feel the long execution time 😉

So I’m using the QTimer – workaround. Now disabling the ActionItems works as expected. Here’s how I have done this:

All ActionItems will be disabled if a Task is running: I’m using a property from NavigationPane. As soon as an ActionItem was triggered, the property ‘running’ was set to true. Then the QTimer was started with a delay of 100 ms. Before the QTimer starts, the System has enough time to disable all Action Items. Because I only want to use one QTimer Object I added a property ‘usecase’ to know which task from C++ should be started.

How to get the information, that the task was finished ? We’re using Qt Signals / Slots: from C++ a signal was emitted and in QML we connect a function (Slot) to this Signal. The connection itself was done in onCreationCompleted{}.

You’ll find different Slots (functions) connected in the sample APP. Here’s a simple function showing a Toast, stopping the ActivityIndicator and setting the property ‘running’ to false:

There are more complex Slots getting result values from C++ if measuring Addresses or Speakers.

Here are some screenshots to see how the results from measuring Addresses are displayed.

We start with an empty Screen:

Starting the Action “Measure Addresses” the ActionItems will be disabled, a Toast will be shown and an ActivityIndicator is running:

This is a long running task and can take up to 2 Minutes !

Getting the Signal that the task is finished, the Toast changes the text and icon, the ActivityIndicator is stopped, a bar chart was drawn and the ActionItems are enabled:

The BarChart should also work in Landscape:

…and also on the Q10:

From this BarChart you easy understand how fast reading a JSON file is directly compared with XML.

You also see that the Z30 is much faster then Q10 (Z10 similar): 81 seconds vs 117 seconds to read a XML file with 10’000 Addresses.

Perhaps you know that Cascades provides no BarChart diagram as UIControl out of the box, but it can be done using plain Containers.

To get all of this smooth working there are some Cascades – layouting – tricks, so let’s take a look at the code step-by-step.

I’m using a StackLayout, but you can also use a DockLayout – it’s up to you. For me in this case a StackLayout seems to fit better. Here’s how the Containers are layed out:

All is contained into an outer Container with Top->Bottom orientation:

the ActivityIndicator: only visible and consuming space if running

two Containers using StackLayout Left->Right for the Header Labels using spaceQuota to adjust them correct

one Container using StackLayout Left->Right for the Footer Labels

in the middle the most difficult Container: the valueContainer – also using a StackLayout Left->Right containing 4 Containers: one for each bar

The valueContainer should consume all available space, so the footer Labels will be always positioned at the bottom. The available space will change if changing the orientation or starting/stopping the ActivityIndicator.

The 4 Containers inside the valueContainer represent the bars and are filled with a background color. The height of the bars must correspond to the values we’re getting from C++ where the largest value should fill out the complete height – all other bars will get a height relative to their values. This means every time the height changes we have to recalculate the height of these Containers.

The trick to get it done is using LayoutHandlers: a LayoutHandler will be notified if the Layout changes and you can ask the handler about the height, which is the value we need in this case. We attach a LayoutHandler to the valueContainer and if the height changes, we recalculate the values.

We store the values as properties at the Page (max, readJson, writeJson, readXml, writeXml) and there’s also a property ‘barHeight’.

The ‘barHeight’ property is bound to the preferredHeight of the valueContainer and initially set to a high value: 1200. Cascades tries to set this value as preferredHeight, but is intelligent only to use a height which fits into the available space. As we already discussed the available space can change – per ex. starting the ActivityIndicator will reduce the height and stopping will enlarge the height. In this case the LayoutHandler will get a notification and we’ll use the actual assigned height to recalculate the values.

There are some special situations where the LayoutHandler from valueContainer won’t be notified about Layout changes, but the outerContainer was notified. So as a little trick we also add a LayoutHandler to the outerContainer and if the Layout changes we simply set the preferredHeight of the valueContainer to a high value (1200) causing Cascades to grab all the available space and so on….

Another situation is if coming back from Landscape to Portrait where we also set this hight value to let Cascades do the calculating of the available space.

The TitleBar was set as sticky – this is a trick to get the correct height on Q10. Otherwise the Q10 will use a floating TitleBar, which means the height of the TitleBar will be ‘available’ and then your footer Labels will be only visible if scrolling the TitleBar away. Setting the TitleBar as sticky solves this.

Please take a deeper look at the sample code …

Business Logic (C++)

Now let’s see what happens at C++ side.

we need the ‘app’ context property to get access to the app. Also all methods must be invokable from QML. Take a look at the sources or the previous articles of this series.

We now want to concentrate an JSON and XML. You must include:

#include <bb/data/JsonDataAccess>
#include <bb/data/XmlDataAccess>

Now we can convert. Here’s an example HowTo read the content of a File, convert from XML to JSON and save the File:

You can load from Files or from a ByteArray you’re getting from a Network request. JsonDataAccess or XmlDataAccess will give you a QVariant. This QVariant can be a QVariantMap if the content is a single Object or a QVariantList if it’s an Array.

In the sample app you’ll find samples HowTo convert from XML to JSON or from JSON to XML, you’ll see how Signals are emitted and HowTo convert the Speakers to a much smaller JSON file with really fast READ access. Here’s the MeasureSpeakerPage:

Don’t forget

Please don’t forget to add the libraries to your .pro file

LIBS += -lbbsystem -lbbdata

Summary

from this sample you have learned

HowTo create a Custom BarChart diagram

Enable / Disable ActionItems

Use of SystemToast in QML

Use of ActivityIndicator

Use of LayoutHandler to provide flexible Layouts for all Devices

Connect QML functions to C++ Signals

HowTo read and write JSON and XML files

HowTo convert JSON to XML or XML to JSON

The main goal of this article was to demonstrate the speed of JSON and the easy way to deal with JSON.

This time I will talk about a Custom Credentials Dialog. There’s already a SystemCredentialsPrompt for Cascades, but we ran into an issue with the password. There were situations where the password field was not masked.

BBJam Asia Login

So I developed a Custom Credentials Dialog to do the Login into BBJam Asia Conference App.

There are different use-cases to do a Login – for the BBJam Asia Conference App Login is optional.

Everyone can use the App and take a look at Sessions, Speakers, Venue, …

To get access to personal schedule, bookmark Sessions as Favorites or fill out Surveys you must be logged in.

Login needs a Username and Password and there should be an easy way to request a new Password directly from Login Dialog.

Login credentials are sent to a Server and feedback if Login was successfull or not comes back asynchron.

If you need a successful Login to start your App you can implement your Login logic similar to the EULA license dialog.

I have extracted the code to do a Custom Login into a sample App and – as usual – all is available at Github (Apache 2 License).

UI (QML)

We need a Dialog with a Title, two entry fields and some Buttons. To solve this the easiest way, we create a Custom Dialog with some properties: alias properties to get the values from fields and a result string.

In your application all text values like “Login to …” should be translated values qsTr(“Login to …”)

The Content is placed inside a ScrollView, so it also works and all is accessible if you’re using a Touch Device in Landscape mode with only 768 (Z10) or 720 (Z30) height and virtual keyboard. The Content Container (id: body) has some padding values – you can adjust them if you want to redesign the layout.

Inside the Container there are the two entry fields for Username (id: usernamefield) and Password (id: passwordField).

Remember that we have access to the values from the Dialog alias properties.

per default the button is disabled. if clicked the result is set to LOGIN and the Dialog will close.

Here’s the LoginDialog from dark Theme on Q10 and light Theme on Z10 using different backgrounds:

The logic to show the Dialog is inside the

assets/main.qml

which is a TabbedPane in ConferenceApp and a simple Page in the sample App.

At first we have to add the Custom Dialog as an attachedObject. Here you also see what happens, if the Dialog was closed. We test for the result and call a method from C++: app.login(…) or app.forgotPassword(…) using the entered values as parameters.

The scret to connect is done inside the onCreationCompleted{}: we connect the signals loginDone() and maiSent() from the C++ Application (app) and call a corresponding function. if login was done with success we set a property, whcih makes another label visible. if a new password was requested we show the SystemToast.

Business Logic (C++)

Now let’s see what happens at C++ side.

we need the ‘app’ context property:

// access to the Application
qml->setContextProperty("app", this);

Setting the context property isn’t enough – we must tell Qt that some of our methods can be invoked. This is done in the applicationUi.hpp Headerfile:

signals must NOT be implemented: using emit loginDone(true) sends them out to all connected to this signal, which can be from C++ and/or QML.

emitting this signals gets the function onLoginDone() called in main.qml.

This is only a sample app: it’s up to you to verify if username and password are valid. Can be done checking a local database or calling a server. If you want to store the username and password you should use QSettings. I explained the use of QSettings in part#1 of this series.

Don’t forget

Please don’t forget to import the bb.system libraries in QML to make the SystemToast run.

import bb.system 1.0

Summary

from this sample you have learned

HowTo reate a Custom Credentials Dialog

Enable / Disable Buttons depending from text Input

Use of SystemToast in QML

Use static asset selectors for Images to support dark and light themes

Writing mobile applications in most cases you want to provide a EULA (End-User-License-Agreement) and the user has to agree to your conditions before using the App. Doesn’t matter if the app is free or not.

It’s recommended to download the Conference App, so you can see how it all works in the context of a complex app.

The EULA is the first Dialog visible after downloading and opening the App, but it was the last part I developed 😉

I allready had implemented an english version hard-coded, but for licenses it’s important that the user understands the content, so the license must be available in all languages.

Last – Minute hacking MUC -> DUBAI -> Hong Kong

I got this information just as I was boarding my flight from Munich to Hong Kong via Dubai. Fortunately the A380 from Emirates has WiFi on board.
So I could communicate and hacking the code.

In Dubai I had to wait some hours for my connection flight, so I did some last coding …

…and finally uploaded the App.

Thanks to BlackBerry I’m allowed to blog about the conference app, patterns used and tips or tricks. This article is the first one of a series.

Usecase

I want to give you some hints HowTo show a localized EULA at startup.

Extracting code from the Conference App into a easy-to-use sample will make it easy for you to use it.

Let’s take a look at the UI first

UI (QML)

We need a Dialog with a Title, the License Text and two Buttons: Agree / Don’t Agree. To solve this the easiest way, we create a Custom Dialog with some properties: alias properties to fill the fields and a result string.

As you can see there’s a second Dialog, a SystemDialog – this Dialog is used if the user doesn’t accept the license. There’s only one chance to retry – if second time the license was not accepted, the App is closed:

Application.requestExit();

If the EULA is ACCEPTED we call a method from C++:

app.setEulaAccepted()

If the EULA is accepted, a value was inserted into Settings, so next time no EULA was displayed.

Now let’s take a look HowTo open the EULA Dialog. At the bottom of main.qml as part of the onCreationCompleted slot:

While testing the Conference App on different devices with different OS we found out that an older 10.1 OS Version has some problems opening the Dialog direct from the onCreationCompleted{}. So we’re doing this async and start a single-shot QTimer, which then opens the dialog.

As soon as you got the list, you can search for the current locale. If an entry is found give this back.

If no entry found check for the first 2 characters only: the language. Per ex. ‘de_DE’ and ‘de_AT’ are valid locales for german language in Germany (DE) or Austria (AT), so if ‘de_DE’ not found, we look for ‘de’. If again no entry found, we use ‘en’ – english as default.