The Decider: External APIs

Exclusive offer: get 50% off this eBook here
Creating Mobile Apps with Sencha Touch 2

Creating Mobile Apps with Sencha Touch 2 — Save 50%

Learn to use the Sencha Touch programming language and expand your skills by building 10 unique applications with this book and ebook.

£16.99    £8.50
by Bryan P. Johnson John Earl Clark | June 2013 | Open Source

One of the key aspects of mobile technology is the ability to tie different systems together into a meaningful application. More and more companies are allowing access to their programs and data through an Application Programming Interface or API. These APIs include things such as:

  • Maps via Google, Yahoo, and other providers

  • Music applications such as Rdio and Spotify

  • Location aware data providers such as Foursquare

  • Social networks such as Facebook and Google Plus

  • Photo services such as Flickr and Picassa

You can get a rough idea of what is available, at http://www.programmableweb.com/apis.

This is just a small sampling of the data available to make your application more useful. The trick is how to get the data and how to use it. In this article we will be using the Foursquare API to explore the use of these types of APIs and how to get started.

In this article by John Earl Clark and Bryan P. Johnson, authors of Creating Mobile Apps with Sencha Touch 2, We will talk about:

  • An overview of external APIs

  • Getting started with the Foursquare API

  • Building the basic application

  • Loading a data store with information from Foursquare

  • Displaying the data to the user

We will start with a general look at how external APIs generally work and what you need to get started with one.

(For more resources related to this topic, see here.)

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.

Ext.define('MyApp.model.Category', {
extend: 'Ext.data.Model',
config: {
fields: ['id', 'name', 'icon']
}
});

We can then set up a store to load data from the API:

var store = Ext.create('Ext.data.Store', {
model: 'Category',
proxy: {
type: 'jsonp',
url : 'http://foursquare.com/vendors/categories' ,
extraParams: {
apiKey: 'XXXXXXXXXXXXXXXXXXXXXXXXX',
appSecret: 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXX'
},
reader: {
type: 'json',
rootProperty: 'categories'
}
}
});

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.

Our main container is a simple card layout:

Ext.define('MyApp.view.ViewPortContainer', {
extend: 'Ext.Container',
config: {
id: 'viewport',
layout: {
type: 'card'
},
items: [ ]
}
});

In this viewport we will add two cards: a navigation view and a form panel. Our navigationvew will serve as our main window for display. We will add additional containers to it via our controller:

{
xtype: 'navigationview',
id: 'mainView',
navigationBar: {
items: [
{
xtype: 'button',
handler: function(button, event) {
Ext.getCmp('viewport').setActiveItem(1);
},
id: 'addContactButton',
ui: 'action',
iconCls: 'add',
iconMask: true,
align: 'right'
}
]
},
items: [
{
xtype: 'container',
id: 'homeScreen',
layout: {
type: 'hbox'
},
items: [
{
xtype: 'button',
action: 'go',
margin: 75,
text: 'Get Started!',
flex: 1
}
]
}
]
}

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:

{
xtype: 'formpanel',
id: 'editContact',
layout: {
type: 'vbox'
},
items: [
{
xtype: 'textfield',
label: 'First Name',
labelWidth: '40%',
name: 'firstname'
},
{
xtype: 'textfield',
label: 'Last Name',
labelWidth: '40%',
name: 'lastname'
},
{
xtype: 'label',
html: 'Choose what kind of food they like:'
},
{
xtype: 'list',
id: 'categoryList',
itemTpl: [
'<div><span class="icon"><img src = "{imgURL}" /></span>
{shortName}</div>'
],
store: 'Categories',
mode: 'MULTI',
flex: 1
},
{
xtype: 'segmentedbutton',
margin: '0 0 10 0',
layout: {
pack: 'center',
type: 'hbox'
},
items: [
{
xtype: 'button',
text: 'Cancel'
},
{
xtype: 'button',
text: 'Save'
}
]
},
{
xtype: 'titlebar',
docked: 'top',
title: 'Add Contact'
}
]
}

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:

https://developer.foursquare.com/docs/explore#req=venues/categories

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.

Each of our categories are laid out like this:

{
id: "4bf58dd8d48988d107941735",
name: "Argentinian Restaurant",
pluralName: "Argentinian Restaurants",
shortName: "Argentinian",
icon: {
prefix: "https://foursquare.com/img/categories_v2/food/
argentinian_",
mapPrefix: "https://foursquare.com/img/categories_map/food/
argentinian",
suffix: ".png",
},
categories: [ ]
}

The main pieces we care about are the id, name, shortname and icon values. This gives us a data model that looks like this:

Ext.define('MyApp.model.Category', {
extend: 'Ext.data.Model',
config: {
fields: [
{
name: 'id'
},
{
name: 'name'
},
{
name: 'shortName'
},
{
name: 'icon'
},
{
convert: function(v, rec) {
return rec.data.icon.prefix+ '32' + rec.data.icon.
suffix;
},
name: 'imgURL'
}
],
proxy: {
type: 'ajax',
url: '/categories.json',
reader: {
type: 'json',
rootProperty: 'categories'
}
}
}
});

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:

{
convert: function(v, rec) {
return rec.data.icon.prefix+ '32' + rec.data.icon.suffix;
},
name: 'imgURL'
}

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:

https://foursquare.com/img/categories_v2/food/argentinian_32.png

By changing the number we can control the size of the icon (this is part of the Foursquare API as well).

We combine this with our XTemplate:

'<div><span class="icon"><img src = "{imgURL}" /></span> {shortName}</
div>'

This gives us a very attractive list for choosing our categories:

Next we need to take a look at the controller for the contact form.

Creating the contact controller

The contact controller handles saving the contact and canceling the action. We start out the controller by declaring our references and controls:

Ext.define('MyApp.controller.Contact', {
extend: 'Ext.app.Controller',
config: {
refs: {
contactEditor: '#editContact',
categoryList: '#editContact list',
cancelButton: '#editContact button[text="Cancel"]',
saveButton: '#editContact button[text="Save"]',
viewContainer: '#viewport'
},
control: {
cancelButton: {
tap: 'doCancel'
},
saveButton: {
tap: 'doSave'
}
}
}
});

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:

doCancel: function() {
this.getContactEditor().reset();
this.getCategoryList().deselectAll();
this.getViewContainer().setActiveItem(0);
}

We just use our references to clear the contact editor, deselect all the items in our category list, and switch back to our main view.

The save function is a little more complex, but similar to the functions we have covered elsewhere in this book:

doSave: function() {
var contact = Ext.create('MyApp.model.Contact', this.
getContactEditor().getValues());
var categories = this.getCategoryList().getSelection();
var categoryIDs = [];
Ext.each(categories, function(category) {
categoryIDs.push(category.get('id'));
});
contact.set('categories', categoryIDs.join(','));
contact.save(function() {
console.log('Contact: ',contact);
});
this.doCancel();
}

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.

Creating Mobile Apps with Sencha Touch 2 Learn to use the Sencha Touch programming language and expand your skills by building 10 unique applications with this book and ebook.
Published: April 2013
eBook Price: £16.99
Book Price: £27.99
See more
Select your format and quantity:

Integrating with Google Maps and Foursquare

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

  • Handle the click of the Get Started button

  • Add our maps panel and offer to adjust the current location via Google Maps API

  • Display a list of friends to include in our search

  • Display the search results in a list

  • Display the details for a selected result

We will start out with the basic skeleton of the controller, create the views and stores, and then finish up the controller to complete the application.

Starting the mainView.js controller

We will start the mainView.js controller file with some placeholders for the stores. We will add views later on and some references for those components.

Keep in mind that when working with placeholders in this fashion the application will not be testable until all the files are actually in place.

We create the mainView.js file in our controllers folder:

Ext.define('MyApp.controller.mainView', {
extend: 'Ext.app.Controller',
requires: 'Ext.DateExtras',
config: {
views: [ 'confirmLocation', 'restaurantList',
'ViewPortContainer', 'friendChooser', 'restaurantDetails'],
stores: [ 'ContactStore', 'RestaurantStore'],
refs: {
viewContainer: '#viewport',
mainView: '#mainView',
startButton: '#homeScreen button[action="go"]',
cancelButton: 'button[action="cancel"]',
finishButton: 'button[action="finish"]',
locationButton: 'button[action="newlocation"]',
nextButton: 'button[action="choosefriends"]',
map: 'confirmlocation map',
restaurantList: 'restaurantlist',
friendList: 'friendchooser list'
}
}
});

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:

Ext.define('MyApp.view.confirmLocation', {
extend: 'Ext.Container',
alias: 'widget.confirmlocation',
config: {
layout: {
type: 'vbox'
},
items: [
{
xtype: 'container',
height: 25,
html: 'Please confirm your location:'
},
{
xtype: 'map',
useCurrentLocation: true,
flex: 1,
plugins: [
new Ext.plugin.google.Tracker({
trackSuspended: false, //suspend tracking initially
allowHighAccuracy: false,
marker: new google.maps.Marker({
position: new google.maps.LatLng(37.44885, -122.158592),
title: 'My Current Location',
animation: google.maps.Animation.DROP
})
})
]
}
]
}
});

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):

{
xtype: 'segmentedbutton',
height: 40,
margin: '10 0 10 0',
layout: {
pack: 'center',
type: 'hbox'
},
items: [
{
xtype: 'button',
text: 'Cancel',
action: 'cancel'
},
{
xtype: 'button',
text: 'New Location',
action: 'newlocation'
},
{
xtype: 'button',
text: 'Next',
action: 'choosefriends'
}
]
}

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:

Ext.define('MyApp.view.friendChooser', {
extend: 'Ext.Container',
alias: 'widget.friendchooser',
config: {
id: 'friendChooser',
layout: {
type: 'vbox'
},
items: [
{
xtype: 'container',
height: 20,
html: 'Please Choose Friends from the list...',
styleHtmlContent: true
},
{
xtype: 'list',
margin: 25,
store: 'Contacts',
itemTpl: [
'<div>{firstname} {lastname}</div>'
],
mode: 'MULTI',
flex: 1,
grouped: true,
emptyText: 'No Contacts to display.<br />Please add some by
clicking the plus icon.'
}
]
}
});

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:

grouper: {
groupFn: function(record) {
return record.get('lastname')[0];
}
}

This configuration returns the first letter of the last name for grouping.

The last thing we need to do with our friendChooser.js file is add the buttons at the bottom to Cancel or Finish the search. The buttons go out in the items section, just below the list:

{
xtype: 'segmentedbutton',
height: 40,
margin: '10 0 10 0',
layout: {
pack: 'center',
type: 'hbox'
},
items: [
{
xtype: 'button',
text: 'Cancel',
action: 'cancel'
},
{
xtype: 'button',
text: 'Finish',
action: 'finish'
}
]
}

As in our previous view, we use a segmentedbutton property with actions assigned to each of our individual buttons.

Once the user clicks on Finish, we will need to return a list of restaurants they can select from.

Creating the restaurant list, store, and details

Our restaurant list will use a store and the Foursquare API to return a list of restaurants based on the shared preferences of everyone the user selected.

The following screenshot exemplifies the preceding explanation:

This component is pretty basic:

Ext.define('MyApp.view.restaurantList', {
extend: 'Ext.dataview.List',
alias: 'widget.restaurantlist',
config: {
store: 'Restaurants',
itemTpl: [
'<div>{name}</div>'
],
onItemDisclosure: true,
grouped: true
}
});

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:

Ext.define('MyApp.store.RestaurantStore', {
extend: 'Ext.data.Store',
requires: [
'MyApp.model.Restaurant'
],
config: {
model: 'MyApp.model.Restaurant',
storeId: 'Restaurants',
proxy: {
type: 'jsonp',
url: 'https://api.foursquare.com/v2/venues/search',
reader: {
type: 'json',
rootProperty: 'response.venues'
}
},
grouper: {
groupFn: function(record) {
var distM = record.raw.location.distance;
var distMiles = Math.round(distM * 0.000621371); //give or take.
return (distMiles == 1)?"1 Mile":distMiles+' Miles';
}
},
sorters: [
{ property: 'name', direction: 'ASC' }
]
}
});

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.

For the model, we can consult the Foursquare API documentation to see the information that is returned for a restaurant (called a venue in Foursquare terms) at https://developer.foursquare.com/docs/responses/venue

You can include any of the fields listed on the page. For this app, we have chosen to include the following code in our model:

Ext.define('MyApp.model.Restaurant', {
extend: 'Ext.data.Model',
config: {
fields: [
{
name: 'id'
},
{
name: 'name'
},
{
name: 'categories'
},
{
name: 'location'
},
{
name: 'contact'
},
{
name: 'menu'
},
{
name: 'specials'
}
]
}
});

You can add more fields if you want to display more information in the details view.

Creating the details view

The details view is a simple panel and XTemplate combination. Using our controller, the panel will receive the data record when a user clicks on a restaurant in the list:

Ext.define('MyApp.view.restaurantDetails', {
extend: 'Ext.Panel',
alias: 'widget.restaurantdetails',
title: 'Details',
config: {
tpl: [
'<div class="restaurant"><span class="name">{name}</span>',
'<tpl for="contact">',
'<span class="phone">- {formattedPhone}</span>',
'</tpl>',
'<div class="icons"><tpl for="categories">',
'<span><img src = "{icon.prefix}32{icon.suffix}" /></span>',
'</tpl></div>',
'<div class="address">Address:<br />',
'<tpl for="location">',
'{address}<br />',
'{city}, {state} {postalCode}',
'</tpl></div>',
'<tpl for="menu">',
'<a class="menu" href="{mobileUrl}">Menu</a>',
'</tpl>',
'<tpl for="specials">',
'<tpl if="count &gt; 0">',
'<div class="specials">Specials:<dl><tpl for="items">',
'<dt>{title}</dt>',
'<dd>{description}<br>{message}</dd>',
'</tpl></dl></div>',
'</tpl>',
'</tpl>',
'</div>'
]
}
});

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:

control: {
startButton: {
tap: 'doStart'
},
cancelButton: {
tap: 'doCancel'
},
locationButton: {
tap: 'doNewLocation'
},
nextButton: {
tap: 'doChooseFriends'
},
finishButton: {
tap: 'doShowRestaurants'
},
restaurantList: {
disclose: 'doShowRestaurantDetails'
}
}

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:

doStart: function() {
var contactStore = Ext.getStore('Contacts');
contactStore.load();
if(contactStore.getCount() > 0) {
this.getMainView().push({ xtype: 'confirmlocation' });
} else {
Ext.Msg.confirm('No Contacts', 'You will need to add some
contacts before we can search for restaurants. Would you like to add
contacts now?', function(btn){
if(btn == 'yes') {
Ext.getCmp('viewport').setActiveItem(1);
}
}, this);
}
}

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:

doCancel: function() {
var count = this.getMainView().items.length - 1;
this.getMainView().pop(count);
}

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:

doNewLocation: function() {
Ext.Msg.prompt(
'',
'Please enter the address you want to search from:',
this.setNewLocation,
this,
100
);
}

If the user enters a new location, we call setNewLocation to process the text the user entered in the prompt textbox:

setNewLocation: function(buttonID, address) {
var geocoder = new google.maps.Geocoder();
var map = this.getMap();
geocoder.geocode({'address': address}, function(results, status) {
if (status == google.maps.GeocoderStatus.OK) {
map.getGeo().suspendUpdates();
map.getMap().setCenter(results[0].geometry.location);
var marker = new google.maps.Marker({
map: map.getMap(),
position: results[0].geometry.location,
title: 'My Current Location',
animation: google.maps.Animation.DROP
});
map.getGeo().setLatitude(results[0].geometry.location.lat());
map.getGeo().setLongitude(results[0].geometry.location.lng());
} else {
Ext.Msg.alert('Error', 'Unable to find address.');
}
});
}

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:

doChooseFriends: function() {
this.getMainView().push({ xtype: 'friendchooser' });
}

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:

doShowRestaurants: function() {
var location = this.getMap().getGeo();
var friends = this.getFriendList().getSelection();
var store = Ext.getStore('Restaurants');
var categories = [];
var dt = new Date();
var first = true;
Ext.each(friends, function(friend) {
if (first) {
categories = friend.get('categories').split(',');
first = false;
} else {
categories = Ext.Array.intersect(categories, friend.
get('categories').split(','));
}
});
store.load({
params: {
ll: location.getLatitude()+','+location.getLongitude(),
client_id: FourSquare.clientID,
client_secret: FourSquare.clientSecret,
radius: 2000,
categoryId: categories.join(','),
v: Ext.Date.format(dt, 'Ymd')
}
});
this.getMainView().push({xtype: 'restaurantlist', store: store});
}

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.

This brings us to our doShowRestaurantDetails function:

doShowRestaurantDetails: function(list, record) {
this.getMainView().push({xtype: 'restaurantdetails', data: record.
data});
}

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:

  • An overview of API basics

  • Putting together the basic application

  • Interaction with Google Maps and Foursquare

  • Building the views, models, and stores

  • Building the application controller

Resources for Article :


Further resources on this subject:


Creating Mobile Apps with Sencha Touch 2 Learn to use the Sencha Touch programming language and expand your skills by building 10 unique applications with this book and ebook.
Published: April 2013
eBook Price: £16.99
Book Price: £27.99
See more
Select your format and quantity:

About the Author :


Bryan P. Johnson

Bryan P. Johnson is a graduate of the University of Georgia. He went to work for MindSpring Enterprises in late 1995, where he met his co-author John Earl Clark. At MindSpring and later, EarthLink; Bryan served in multiple positions for over seven years, including the Director of System Administration and Director of Internal Application Development. After leaving EarthLink, he took some time off to travel before joining John to start Twelve Foot Guru.

Bryan has worked with Sencha's products since the early days of YUI-Ext and has used Sencha Touch since its first betas.

John Earl Clark

John Earl Clark holds a Master's Degree in Human Computer Interaction from Georgia Tech and an undergraduate degree in Music Engineering from Georgia State University. John and his co-author, Bryan Johnson, worked together at MindSpring and later EarthLink, starting out in Technical Support and Documentation, before moving into Application Development and finally management of a small development team. After leaving Earthlink in 2002, John began working independently as a consultant and programmer, before starting Twelve Foot Guru, LLC with Bryan in 2005.

John has been working with Sencha Touch since the first early beta releases. He has also worked with Sencha’s ExtJS since the early days when it was still known as YUI-Ext. John has also written a previous book with Bryan Johnson called Sencha Touch Mobile JavaScript Framework.

When he is not buried in code, John spends his time woodworking, playing guitar and brewing his own beer.

Books From Packt


Appcelerator Titanium Smartphone App Development Cookbook
Appcelerator Titanium Smartphone App Development Cookbook

Corona SDK Mobile Game Development: Beginner's Guide
Corona SDK Mobile Game Development: Beginner's Guide

Learning Kendo UI Web Development
Learning Kendo UI Web Development

Joomla! Mobile Development Beginner’s Guide
Joomla! Mobile Development Beginner’s Guide

Creating Mobile Apps with jQuery Mobile
Creating Mobile Apps with jQuery Mobile

Sencha Touch Mobile JavaScript Framework
Sencha Touch Mobile JavaScript Framework

Sencha Touch Cookbook
Sencha Touch Cookbook

Sencha MVC Architecture
Sencha MVC Architecture


Code Download and Errata
Packt Anytime, Anywhere
Register Books
Print Upgrades
eBook Downloads
Video Support
Contact Us
Awards Voting Nominations Previous Winners
Judges Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software
Resources
Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software