CPSC 110-08: Computing on Mobile Phones
Shopping With Friends Tutorial

Databases and Persistent Data

This tutorial teaches you how to share data with friends using App Inventor's TinyWebDb, a simple online (web) database. We used the TinyDb in two of our previous apps -- No Text While Driving and Broadcast Hub.

Here's a brief review about TinyDB and Lists.

The Shopping With Friends App is an example of client/server computing. The mobile phone is the client and one or more phones will interact with a database that is stored on a server on the web.

To follow along with this tutorial, download the app's sourcecode and import it into App Inventor as one of your projects.

Introduction

Databases are used to organize and manage information. They are a form of persistent or long term memory because the data persist between the stopping and starting of the application. Once data are put into a database, they remain there until the database is deleted or the App is removed. This differs from short term memory, such as global variables whose values last only as long as your app is running.

Tags and Values

The the DB and Lists tutorial introduces TinyDB, App Inventor's on-phone database.

The TinyDb stores data in a tag-value scheme. The tag is used as a key by which the data can be retrieved. For example:

TagValue
OneThis is the first string
TwoThis is another string

To store a value in a TinyDb, you would use the StoreValue procedure, which takes two arguments, a tag and a value. The tag is always text, but the associated value can be a number, or a string, or even a list.

To retrieve a value from the Db, you use the GetValue function, which takes the tag as an argument and returns the associated value as its result. In this case we are retrieving the list of data associated with "Hamlin":

Setting up a TinyWebDb

The TinyWebDB component stores data on the Google cloud on a server whose URL is http://appinvtinywebdb.appspot.com.

The problem with storing your data there is that it is accessible to all App Inventor developers. Other developers could overwrite your data.

Although we won't go into details here, it is relatively easy to create your own TinyWebDB database service using some code that Prof. Wolber of the University of San Francisco has created. You don't even need to know how to code-- you can just download some sample code and then upload it to the "cloud" with Google's App Engine. For instructions, see http://appinventorapi.com/program-an-api-python/.

Shopping With Friends

Let's suppose you and your friends are going shopping at the mall and want to keep in touch and share your "finds" -- bargains, cool stuff, etc. The ShoppingWithFriends app will use a TinyWebDb component to store finds that you and your friends post to the web. For this example, we'll only store the name of the item, what store it was found at and its price.

For this app we will use a server that I created at http://ram8647-appinventortest.appspot.com. Remember this URL -- we'll need it below.

The user interface will be based on the following design:

The UI contains two ListPicker components which are pre-set to a list of shopping items -- i.e., shoes, bags, blouses, etc -- and stores -- e.g., Macy's, Nordstrom. When you choose an item off the ListPickerItem, the app will look up that item on the web database, DBWeb, and display a list of finds on the TextBoxDisplay.

There are also a couple of TextBox components where the user can enter the location (what store) and price of cool bargains they find. When the Report a Find button is clicked, the where and price information is sent to the DbWeb database to be shared among your friends.

Globals

For this app we need several variables and constants. A constant is a global variable whose value does not change as the program executes. To distinguish them from variables, we given them UPPERCASE names. In this case we have one constant, which is to represent a Db Tag:

The item and itemsList variables are used to temporarily store the current item selected on the list picker and the list of that type of item that was downloaded from the web db. Global variables are not persistent data.

Our Data Model

When using a database it's important to design a model for your data. For complex applications, this can get involved. But for this simple app we can use a simple list of lists model, where for each tag, say "shopping-shoes", we store a list of finds where each find is a list of locations (stores) and prices. So in general our data model will look like this:

[ [store1, price1], [store2, price2], ..., [storeN, priceN]]

A particular case might be something like:

[ [Macy's, 10.98], [Nordstorm, 6.99], [Lord & Taylor, 15.99] ]

In this case we have a list containing three elements where each element is a list containing two items.

Asynchronous Web Retrieval

Retrievals from TinyWebDB are asynchronous. This means they are not synchronized, -- i.e., you can't really tell how long it will take for the DBWeb.GetValue operation to get the requested value.

More precisely, in programming this means that the program will not wait for the DBWeb.GetValue operation to complete. The program will continue independently of that operation. Note that this is different from the TinyDb, which is stored on the phone. TinyDb operations are synchronous operation -- i.e., the program will wait until it completes before performing the next operation.

App Inventor uses the GotValue block to handle asynchronous retrievals from a TinyWebDb. This handler is invoked whenever App Inventor receives data from the DBWeb database.

Note that this handler has two arguments, the tagFromWebDb and the valueFromWebDB. If the program were making lots of different queries, you would want to test which tag is being retrieved and take different actions for different tags. But in this case all our retrievals are of the form "shopping-X" and we are going to take the same action in each case.

But we do want to test whether the retrieval of, say "shopping-bags", returned anything. If the tag you are retrieving is not stored in the DB, the value returned will be the empty string. So if the value returned is the empty string we set itemsList to an empty list and we report "Not Found".

Otherwise, recall from our data model that we are storing a list of lists for each shopping item. So the value returned from the database will be a list. We store it in itemsList and display it in the TextBoxDisplay.

Handling the List Picker

The ListPicker can be used to change the type of item being shopped for.

Whenever the user makes a selection from the list picker, we display the selection as the list picker's label, set the global variable item to its value, and look up that type of item on DbWeb. Again, the DBWeb.GetValue tag is something like "shopping-shoes", where "shoes" is the item that the user just selected.

Reporting a New Find

To report a new find the user selects the store name and fills in a price and clicks ButtonReport, which would lead to the following actions:

When the update button is clicked, the program creates a new list of the form "[where, price]" and adds it to the current itemsList. At this point itemsList could be an empty list or it could be a list of the finds that were previously download from the DBWeb. Se we are adding our new find to the current list of finds.

After updating the itemsList, we want to store on the web database with the command DBWeb.StoreValue. Note again how we construct the tag here by joining "shopping-" with the type of item to get something like "shopping-belts".

Finally, we want to display the newly updated list, including any finds that may have been added by our friends. So we also call the DBWeb.GetValue procedure. Its result, remember, will be retrieved by the DBWeb.GotValue event handler.

Let's Try It

To download and run the app on your Android phone, use the Android's Barcode Scanner to scan this QR Code:

qrcode

If you don't have Barcode Scanner on your phone, you can download it for free from the Android market.

Let's Revise It

Download the app's sourcecode and import it into App Inventor as one of your projects. Here's a couple of revisions you could try.

  1. Add more items to the ListPicker: You can change the ListPickers' Elements properties in the Browser.

  2. Prettify It: Add some color and think about other ways to improve the app's appearance. Change the "Find" label to something more appealing or descriptive, like "Bargain"?

  3. Fix a Bug: Right now, if you Report a Find before selecting an item and a store, the app will store an empty list. To fix this, you would want to revise the ButtonReport.Click block.

  4. Avoiding duplicate items: One improvement would be to avoid duplicate entries when the user clicks on the ButtonReport button.

    To do this you'll need to modify the ButtonReport block, adding some code to test if the new entry is already in the list. Blocks that look like these should be useful:

    Brainstorm: Propose a change and we'll try to do it together.