The Decider: External APIs

Packt Publishing

If you have basic to intermediate knowledge of Sencha Touch, this tutorial will give you a deep insight into its full capabilities when creating mobile apps. Learn the language through building ten unique applications.

Using an external API

APIs are provided as a service from many different companies. This is not an entirely altruistic move on the part of the company. The expectation is that by providing the information and access to the company's data, the company gets more usage for their service and more customers.

With this in mind, most (if not all) companies will require you to have an account on their system in order to access their API. This allows you to access their systems and information from within your application, but more importantly from the company's perspective, it allows them to maintain control over how their data can be used. If you violate the company's usage policies, they can shut off your application's access to the data, so play nice.

The API key

Most APIs require a key in order to use them. An API key is a long string of text that gets sent as an extra parameter on any request you send to the API. The key is often composed of two separate pieces and it uniquely identifies your application to the system much like a username and a password would for a regular user account. As such it's also a good idea to keep this key hidden in your application so that your users can't easily get it.

While each company is different, an API key is typically a matter of filling out a web form and getting the key. Most companies do not charge for this service. However, some do limit the usage available to outside applications, so it's a good idea to look at any restrictions the company sets on their service.

Once you have an API key you should take a look at the available functions for the API.

API functions

API functions typically come in two types – public and protected:

The public functions can simply be requested with the API key

The protected functions will also require that a user be logged into the system in order to make the request

If the API function is protected, your application will also need to know how to log in correctly with the remote system. The login functions will usually be a part of the API or a web standard such as Facebook and Google's OAuth.

It should be noted that while OAuth is a standard, its implementation will vary depending on the service. You will need to consult the documentation for the service you are using to make sure that the features and functions you need are supported.

Be sure to read through the service's API documentation to understand which functions you will need and if they require a login.

Another thing to understand about APIs is that they don't always do exactly what you need them to do. You may find that you need to do a little more work than you expect to get the data you need. In this case, it's always good to do a little bit of testing.

Many APIs offer a console interface where you can type commands directly into the system and examine the results:

This can be really helpful for digging into the data, but consoles are not always available for every API service. Another option is to send the commands in your application (along with your API credentials) and examine the data returned in the Safari console.

The drawback of this method is that the data is often returned as a single-line string that is very difficult to read as shown in the screenshot:

This is where a tool like JSONLint comes in handy. You can copy and paste the single-line string from your Safari console into the page at http://jsonlint.com and have the string formatted so that it is much easier to read and validate the string as JSON at the same time:

Once you get a hold of what data is being sent and received, you will need to set it all up in Sencha Touch.

External APIs and Sencha Touch

As we have talked about earlier in the book, you cannot use a standard AJAX request to get data from another domain. You will need to use a JSONP proxy and store to request data from an external API.

Using the API or the Safari console, you can get a good idea of the data that is coming back to you and use it to set up your model. For this example, let's use a simple model called Category.

This will set up a store with our Category model and call the url property for our external API. Remember that we have to send our credentials along with the request so we set these as extraParams on the proxy section.

The apiKey and appSecret properties shown here are examples. You will need your own API key information to use an API.

We also need to set a property called rootProperty in the reader section. Most API's send back a ton of detailed information along with the request and the store needs some idea of where to start loading in the category records.

We can also add additional parameters later by calling the setExtraParam() function on our store proxy. This will let us add additional parameters to be sent to our external API URL.

Please note that setExtraParam() will add an additional parameter but setExtraParams() will replace all of our extraParams with the new values.

The basic application

The Decider application is designed to use a combination of local storage, Google's Map API, and the Foursquare API. The application will take a list of people and their food preferences, and then use Foursquare and Google Maps to find nearby places to eat that will match everyone's food preferences.

This screenshot provides a pictorial representation of the preceding explanation:

Our contacts and categories will be stored using local storage. External APIs from Google and Foursquare will generate our maps and restaurant listings respectively. We will start with a quick overview of the basic application structure and forms, before diving into the store setup and API integration.

This mainView contains our navigationBar and our homeScreen container with the big Get Started button. This button will add new containers to the navigation view (we will look at this later in the controller).

Remember that Sencha Touch automatically creates a back button for each container that is added to the navigation view. This means that we don't have to write an extra code for it.

The second item that is added to our viewport is our form panel. This will contain text fields for first and last name, as well as a selectable list for our different food categories:

We close out the form with a segmentedbutton property, which has options for Save and Cancel. We will add the handler functions for these buttons later on in our controller.

We also include a title bar at the top of the form to give the user some idea of what they are doing.

One of the key pieces of this form is the categories list, so let's take a closer look at how it works.

Creating the categories list

Since we will be getting our list of potential restaurants from the Foursquare API, we need to use their categories as well so that we can match things up with some degree of accuracy.

The Foursquare API can be found at https://developer.foursquare.com/. As mentioned before, you will need a Foursquare account to access the API. You will also need an API key in order to integrate Foursquare with your application.

We can use the Foursquare's API to get a list of categories, however the API returns a list of a few hundred categories including Airports, Trains, Taxis, Museums, and Restaurants. Additionally, each of these has its own subcategories. All we really want is the subcategories for Restaurants.

To make things more complicated, Foursquare's API also returns the data like this:

categories: [{category 1},{category 2},{category 3},{category 4}…]

This means we can only get at a specific category by its order in the array of categories. For example, if Restaurants is the twenty-third category in the array, we can get to it as: categories[23], but we cannot get to it by calling categories['Restaurants']. Unfortunately, if we use categories[23] and Foursquare adds a new category or changes the order, our application will break.

This is a situation where it pays to be adaptable. Foursquare's API includes a console where we can try out our API requests. We can use this console to request the data for all of our categories and then pull the data we need into a flat file for our application. Check this URL to see the output:

We can copy just the Restaurant information that we need from categories and save this as a file called categories.json and call it from our store.

A better solution to this conundrum would be to write some server code that would request the full category list from Foursquare and then pull out just the information we are interested in. But for the sake of brevity, we will just use a flat json file.

Notice that we also add a function to create an image URL for the icons we need. We do this with the convert configuration, which lets us assemble the data for image URL based on the other data in the record:

The convert function is automatically passed both the data value (v), which we ignore in this case, and the record (rec), which lets us create a valid Foursquare URL by combining the icon.prefix value, a number, and the icon.suffix value in our record. If you take a look at our previous category data example, this would yield a URL of:

Remember that our refs (references) provide a handy shortcut we can use anywhere in the controller to get to the pieces we need. Our control section attaches tap listeners to our cancel and save buttons.

Next we need to add our two functions after the controls section. The doCancel function is really simple:

As with our previous save functions, we create a new MyApp.model.Contact and add the values from our form. However, since our list isn't really a standard form component we need to grab its selections separately and add them to the contact data as a comma-separated list.

We do this by creating an empty array and using Ext.each() to loop through and run a function on all our categories. We then use join to implode the array into a comma-separated list.

Finally, we save the contact and run our doCancel function to clean up and return to our main view.

Now that we can add contacts we need to create a controller to handle our requests to the Foursquare and Google APIs, and get the data back to our users.

Integrating with Google Maps and Foursquare

Our application still has a couple of tasks to accomplish. It needs to:

At the top of this configuration we require Ext.DateExtras. This file provides us with formatting options for date objects. If this file is not included, only the now() method for date objects will be available in your application.

In our views section we have added placeholders for confirmLocation, restaurantList, friendChooser,and restaurantDetails. We will add these files later on, along with the RestaurantStore file listed in our stores section.

We also have a number of references for these views, stores, and some of their sub-components. We will need to create these views before getting to the rest of our controller. We will take these views in the order the user will see them, starting with the confirmLocation view.

Creating the confirmLocation view

The confirmLocation view first appears when the user clicks on the Get Started button. This view will present the user with a map showing their current location and offer an option to switch to a different location if the user desires.

The following screenshot gives a pictorial representation of the preceding code:

In order to give ourselves a bit more flexibility, we will be using the Google Maps Tracker plugin as part of this view. You can find this plugin in your Sencha Touch 2 folder in examples/map/lib/plugin/google/Tracker.js. Copy the file into a lib/google folder in your main application folder and be sure to add it into the requires section of your app.js file:

requires: ['Ext.plugin.google.Tracker']

This plugin will let us easily drop markers on the map.

Once the Google Tracker plugin file is included in the application, we can set up our confirmLocation.js view like so:

The view itself is a simple container with some HTML at the top asking the user to confirm their location. Next we have a map container that uses our Google Tracker plugin to configure the map and animate the location marker to drop from the top of the screen to the current location of the user. The position configuration is a default location, which is used when the user denies the application access to their current location. This one is set to the Sencha Headquarters.

Next we need a few options for the user to choose from: Cancel, New Location, and Next. We will add these as a segmented button under our map container. We add the code to the end of our items container (after the map container):

Each of our buttons has an associated action. This allows us to assign functions to each button within the mainView.js controller. By creating buttons in this fashion, we maintain separation between the display of the application and the functionality of the application. This is really helpful when you want to re-use a view component.

The next view the user encounters is the Friends Chooser.

Creating the Friends Chooser view

The friendsChooser.js file uses a similar list to our previous category chooser. This lets our users select multiple people to include in the restaurant search:

Our friendChooser extends the Ext.Container component and allows the user to select from a list of friends:

As with our previous panel, we have a container with HTML at the top to provide some instructions to the user. Below that is our list container, which, like our category list, allows for selection of multiple items via the mode: 'MULTI' configuration. We also set grouped to true. This allows our store to group the contacts together by last name.

If you take a look at the ContactStore.js file, you can see where we do:

This component uses a simple list with a configuration option for onItemDisclosure:true. This places an arrow next to the restaurant name in the list. The user will be able to click on the arrow and see the details for that restaurant (which we will create after the store).

We also set grouped to true, only this time our store will use a function to calculate and sort by distance.

Creating the restaurant store and model

The restaurant store is where we set up our request to the Foursquare API:

The RestaurantStore.js file sets a model and storeId field for our store and then defines our proxy. The proxy section is where we set up our request to Foursquare.

As we mentioned at the start of the article, this needs to be a jsonp request since it is going to another domain. We make our request to https://api.foursquare. com/v2/venues/search and we are looking for the responses.venues section of the JSON array that gets returned.

You will note that this store currently has no other parameters to send to Foursquare. We will add these later on in the controller before we load the store.

Since the tpl tag is basically HTML, you can use any CSS styling you like here. Keep in mind that certain fields such as contact, location, and categories can have more than one entry. You will need to use <tpl for="fieldname"> to loop through these values.

Now that the views are complete, we need to head back to our controller and add the functions to put everything together.

Finishing the main view controller

When we started out with our main controller, we added all of our views, stores, and references. Now it's time to add the functionality for the application. We start by adding a control section to the end of our config:

The controls are based on the references in the controller and they add functions to specific listeners on the component. These are each in the format of:

reference: {eventName: 'functionName'}

Once these controls are in place, we can add our functions after the config section of our controller.

Our first function is doStart. This function loads our Contacts store and checks to see if we have any existing contacts. If not, we alert the user and offer to let them add some. If they have contacts we create a new instance of our confirmLocation container and push it onto the main navigation view:

Remember that since the mainView is a navigation view, a Back button will automatically be created in the top toolbar. This function will show the user our initial map panel with the users current location.

This panel needs four functions: one to cancel the request, one to pop up a new location window, one to set the new location, and one to move on to the next step:

We actually want to be able to use the doCancel function from anywhere in the process. As we add new panels to our mainView navigation, these panels simply pile up in a stack. This means we need to get the number of panels currently on the mainView stack. We use length-1 to always leave the initial panel (the one with our big Get Started button) on the stack. We use pop to remove all but the first panel from the stack. This way the Cancel button will take us all the way back to the beginning of our stack, while the Back button will take us back just to the previous step.

The next function is doNewLocation(), which uses Ext.Msg.prompt to ask the user to enter a new location:

This code gets our map and encodes the text the user passed us as a geocode location. If Google returns a valid address, we center the map on the location and drop a marker to show the exact location. We also set the latitude and longitude so that we can reference them later.

If we fail to get a valid address, we alert the user so they can fix it and try again.

Once the user is happy with the location they can click on the Next button, which fires our doChooseFriends function:

This function pushes our friendchooser view onto the stack for display. The friendchooser view allows the user to select multiple friends and click on Cancel or Finish.

Since we have already taken care of our Cancel button with our doCancel function, we just need to write the doShowRestaurants function.

This function starts by looping through the selected friends. For the first one in the list, we grab the restaurant categories we have stored for the friend and convert it from a comma-separated list (which is how we stored it) into an array.

This lets us grab every subsequent selection and run Ext.Array.intersect() to find the common categories between all of the selected friends:

Next, we load the store based on the common categories by categoryID, the location data we have stored in our map, client_id, and client_secret that comprise our API key for Foursquare and a radius value (in meters).

We also send a required field called v that is set to the current date.

Finally, we push our restaurant list component onto the stack of containers. This will display our list of results and allow the user to click on for details.

When the user taps one of the disclosure icons in our list of restaurants, we push a restaurantdetails view onto the stack of containers and set its data to the record that was tapped. This displays the details for the restaurant in our details XTemplate

Homework

There are a number of additional features that can be added to this type of application, including:

Editing for contacts (or automatically pulling friends from Facebook)

Setting up a live feed for the categories menu

Adding additional venues other than restaurants

Combining the application with additional APIs such as Yelp for reviews

Just remember the key requirements of using additional APIs: the API key(s), studying the API documentation, and using the JSONP store for grabbing the data.

Summary

In this article we talked about using external APIs to enhance your Sencha Touch applications. This included:

Alerts & Offers

Series & Level

We understand your time is important. Uniquely amongst the major publishers, we seek to develop and publish the broadest range of learning and information products on each technology. Every Packt product delivers a specific learning pathway, broadly defined by the Series type. This structured approach enables you to select the pathway which best suits your knowledge level, learning style and task objectives.

Learning

As a new user, these step-by-step tutorial guides will give you all the practical skills necessary to become competent and efficient.

Beginner's Guide

Friendly, informal tutorials that provide a practical introduction using examples, activities, and challenges.

Essentials

Fast paced, concentrated introductions showing the quickest way to put the tool to work in the real world.

Cookbook

A collection of practical self-contained recipes that all users of the technology will find useful for building more powerful and reliable systems.

Blueprints

Guides you through the most common types of project you'll encounter, giving you end-to-end guidance on how to build your specific solution quickly and reliably.

Mastering

Take your skills to the next level with advanced tutorials that will give you confidence to master the tool's most powerful features.

Starting

Accessible to readers adopting the topic, these titles get you into the tool or technology so that you can become an effective user.

Progressing

Building on core skills you already have, these titles share solutions and expertise so you become a highly productive power user.