7 Tips for Utilizing Amazon Alexa to Engage with Customers

7 Tips for Utilizing Amazon Alexa to Engage with Customers

Amazon first released Alexa virtual assistant and smart speaker Echo in late 2014. An in-home virtual assistant is an impressive tool, but creating a seamless user experience with it can be a challenge. So how do companies overcome this challenge? What engagement can you provide with a voice programming interface to fully utilize Alexa and engage customers with your brand?

Why Should Brands Care?

Amazon has a wide variety of Alexa products, but the Echo Dot keeps the price of admission as low as $49.99. The Echo Dot also frequently goes on sale, lowering the bar to $39.99 or even $29.99. At such a low price point, it’s no wonder NPR and Edison Research have found that 39 million adults in the US now own a smart speaker. And of those 39 million, 65% would not want to go back to life without it.

Establish a Brand Presence on Alexa

Alexa skills give you an opportunity to produce a user experience with voice. Users can simply activate skills for use on their smart speaker through the Amazon Alexa app or by simply asking their device to enable a particular skill. Alexa skill development gives you the opportunity to advertise and engage with your customers and provide value through your brand.

Build to Consider

The development of the skill is relatively simple. As an Alexa developer, I am excited to find that Amazon provides a great guide to get you started. A skill is just a REST endpoint, accessed over HTTPS, combined with a list of user actions and activation phrases. The endpoint can be hosted on any server that supports HTTPS, but an easy way to get started fast is to utilize AWS Lambda. User actions, known as intents, and activation phrases, known as utterances, are then added through the AWS console. When a user talks to your skill, Amazon will attempt to match what was said to an utterance to determine the intent. The intent and phrase are then sent to the REST endpoint; that endpoint then has a chance to do something with the information before sending back a response to be read to the user.

Once done with the Alexa skill development portion, the next challenge is creating a good user experience. Why is it important to create a seamless user experience and how much can you expect out of Alexa voice programming?

Here are just a few examples of how to master Alexa skills and make sure you engage with your user while building customer loyalty:

Tip #1: Provide a Great Experience on Open

Most users immediately ask skills questions directly, however it is important to note that skills can also be opened without a question. When a skill is opened for the first time, be sure to provide instructions on how to use your skill. Give examples of what kinds of questions can be asked. If the user is returning, let them know about any additions to your skill or ongoing promotions with your brand.

Tip #2 Answer Common Questions

Think about what kind of questions customers have. Instead of your customer searching through a mobile app or website for the answer, they can just ask! It’s important to start off with the basics and be prepared to handle general questions such as:

  • “Where is the closest store?”
  • “What are the hours of the store?”
  • “What is the phone number of the store?”

Try to think about how your customer will naturally try to ask a question. Customers may ask “When does the store close?” and expect to receive the store hours. Consider error handling as well. Your user may be asking for store hours, but the provided store cannot be found. Take advantage of the ability to respond with a question and receive a response. For example “I’m sorry, which store would you like hours for?”

Tip #3 Surface Interesting Data

Use analytics from your stores to help customers out where possible. Leverage store traffic data so that your customers can ask how busy a store is. Customers may be more interested in shopping when the store is less busy. If the store is busy, consider responding with an estimate of when there will be fewer people in the store.

Utilize your inventory database to allow customers to ask if a product is in stock. If there is low stock then engage with your customer by encouraging them to order fast or reserve their selected item. If a product is not in stock, let your customer know when they can expect the product to return or suggest a similar product. The more helpful you are to your users, the more likely they are to use your skill in the future — a guaranteed way of keeping people loyal to your brand.

Tip #4 Promote upcoming events and ongoing sales

Allow customers to ask about promotions. When customers ask about upcoming events, provide relevant information such as the date, time and location. When a customer asks about sales, be creative with your response. Instead of listing off items, pick a few to highlight based on their previous purchases. Allow customers to ask about sales by department, category, or deal, such as “what product is on sale?”, to get more specific answers on sales.

Tip #5 Extend the App Experience

If there is an OAuth login for your services, customers will be able to link their existing accounts using the Alexa Skill. This allows support for features that require authentication. Allow customers to add items to their shopping list or even do online ordering. Leverage information about the logged-in customer when responding to other questions. When a customer asks about discount opportunities, spike their interest by targetting a known previous purchase. You can also allow an authenticated customer to ask questions about their favorite store.

Tip #6 Provide Some Entertainment

If possible, add something extra to make the experience fun. Allow customers to ask for interesting facts about your brand or any questions related to your industry. To make things more engaging, you could even add a weekly trivia question to encourage users to check back in regularly. It’s important not to overdo it.

Tip #7 Utilize Flash Briefings

Flash briefings are very short podcasts that Alexa users can subscribe to, they are a great way to communicate with customers who are opted in. A user will subscribe to flash briefings from various skills to build their flash briefing. Then when a user asks, the latest series of flash briefings will be played. These are generally for news, however, they can be used to talk about upcoming promotions or events. Take a minute or two each week to let your most loyal customers know what’s going on with your company.

It is evident and backed that people are ready to accept virtual assistants in their homes. Companies taking advantage of this interface will ultimately be the ones that pioneer the next generation of mastering brand engagement and capturing this fast-growing market. I hope you find these 7 tips helpful in building customer loyalty and engagement, leave us a comment to share your thoughts!

Andrew Rumbley

Andrew Rumbley

Senior Mobile Developer

Andrew is a Senior Mobile Developer focused on creating quality user experiences. He is a full stack developer with experience working on every piece of a mobile application from backend APIs to frontend UI. Andrew is passionate about creating high performing native applications. He also has the distinguished mantle of being the longest-tenured developer at Shockoe.

Comparing React Native to Axway Titanium

Comparing React Native to Axway Titanium

Here at Shockoe we often use cross-platform tools to build our apps. Using a cross-platform tool allows us to have one code base for apps that run on multiple platforms. There will be some platform specific code, but most things can be shared. Our cross-platform tool of choice is Axway Titanium. It used to be that cross-platform tools heavily leveraged WebViews. Tools like Cordova (ex PhoneGap) allow the developer to write a mobile website using HTML, CSS, and JavaScript. Then PhoneGap handles showing this content to the user inside of a native WebView. Instead of the WebView approach, Titanium gives you a JavaScript context and provides a bridge that handles interactions between the JavaScript environment and native components. Titanium stood out because it actually interacted with native components. But now Titanium is not the only framework out there that takes this approach. A couple years ago Kyle took an early look at React Native. Let’s take another look and see how React Native has come along.

Getting Started

Start off by heading over to the React Native Getting Started page. They offer two options: Quick Start and Building Projects with Native Code. I have not tried the, now default, Quick Start option. Several documentation pages refer to needing to “eject” your application if it was created from the Quick Start. For that reason alone I have only used the Building Projects with Native Code option.

There are a few dependencies to install, but the guide walks you through what you need. You will need NodeJS and the watchman package for observing changes. You will also need to install the react native cli. Additionally, you will need Xcode if building for iOS and Android Studio if building for Android.

Once you’ve got the dependencies installed you create a new project with the CLI:
react-native init AwesomeProject

Running the App

With no changes to the code base, you can immediately build the app you just created. In a Titanium project, all builds are handled through the Axway Appcelerator CLI or Axway Appcelerator Studio. This is not the case with React. It seems you can only build to an iOS simulator, Android emulator, or Android device with the React Native CLI. To do this you use either:
react-native run-ios
To target iOS simulator. Or:
react-native run-android
To target an Android device or emulator.

The options provided with these commands are a little lacking compared to the options with the Axway Appcelerator CLI. In my time with React Native, every simulator build chose the iPhone 6 simulator. I could not find an option to specify a different simulator with the CLI. Additionally, the CLI does not handle multiple connected Android devices well. You need to only have a single connected Android device or running emulator.

So how do you target other iOS simulators or build to an iOS device? Open Xcode! From there you use the same build options that a native developer would use. This is a huge difference from Titanium that basically discourages the use of Xcode for anything but building native modules. If you’ve never done native iOS development this can be a little daunting at first. It’s simple enough to find the play button and drop-down to select your build target. But what if you want to do an adhoc distribution build? Fortunately, there are plenty of resources out there for learning Xcode.

How about Android builds? This is an area that I am not as familiar with. Because the React Native CLI is capable of building to a device, I haven’t tried to build the project with Android Studio. I have generated a signed APK. The React Native documentation has a guide, but it comes down to using gradle.

Editing the App

React Native does not provide an IDE like Axway Appcelerator Studio. The documentation does suggest taking a look at Nuclide. Nuclide is a package for Atom that claims to setup an environment for developing React Native. I found I wasn’t taking advantage of its features, so I uninstalled it after a couple days in favor of just Atom.

So you can open the code in a text editor, where do you go from there? With a Titanium project, at least an alloy one, the entry point is alloy.js. From there the index controller has loaded first automatically. React Native provides entry points at index.android.js and index.ios.js. From there you can load whatever components you wish. The simplest thing to do is to edit some of the text provided with the sample project. Once you’ve made an update you can easily see your changes without rebuilding your app!

Axway Titanium provides a live view feature to see your app update as code changes. React Native offers a similar feature. On simulator you can press command + R to reload the code from the React Native packager. On an android emulator you can achieve the same thing by tapping R twice. Reloading can also be accessed from a built-in developer menu! To access the developer menu simply shake your device. You will see options to reload, enable remote JS debugging, enable live reload, and more.

Debugging Your Code

Axway Titanium attaches a console to builds made directly to a device, emulator, or simulator. The React Native process ends as soon as a build is installed and does not attach a console. Instead, you can enable remote debugging through the developer menu and debug your app in Google Chrome. You do not see a DOM representation of the app, but you do get access do the console and debugging tools! The debugging is done over TCP, so you don’t need to have built on a device connected to your computer. Inside the developer menu, you can change the URL used for remote debugging so you can debug as long as the device and machine running Google Chrome are on the same network.

Moving Forward

This has only been a brief look at getting started with React Native. In the future, I would like to revisit this topic to discuss more configuration, component driven design, and interacting with native code. React Native is very young, but it has come a long way in a short period of time. I am very excited to see how it matures as a cross-platform framework.

Want to stay connected on all things mobile?

 

Sign up for the Shockoe newsletter and we'll keep you updated with the latest blogs, podcasts, and events focused on emerging mobile trends.

 

Network Discovery with Titanium

Network Discovery with Titanium

So you have 2+ devices that need to find each other on a local network. At least one of them is a mobile device running iOS or Android (sorry Windows users!). One device needs to be hosting a server to handle connections. How do they go about finding each other? You could display a local IP address on the server and have the client devices manually type in the address, but that is terrible UX. There has to be a better way!

Network Discovery is the answer to this problem. Specifically Multicast DNS (mDNS) over Zero-configuration networking (zeroconf). Fortunately these are open technologies that have some implementation available on most platforms. Unfortunately these implementations come with their own names and rarely do they offer any indication that they support each other. Zeroconf, Bonjour, Avahi, or mDNS can be used mostly interchangeably. They’re all implementations of the same technology.

The most well known zeroconf implementation is Apple’s Bonjour. Bonjour is an excellent implementation of zeroconf, but it is only available on iOS, OSX, and Windows (not Windows Phone!). Appcelerator Titanium provides support and documentation for both the BonjourService and the BonjourBrowser.

We’ve recently been working on a project that has to connect iOS and Android devices to a desktop application running on a Windows machine. Our approach was to host an HTTP server on the Windows machine and broadcast its presence over zeroconf, specifically using Bonjour for Windows. On the mobile device we will search for this service, find an address that we can use to reach it, and then communicate over a standard HTTP connection. All examples below will follow this implementation strategy.

Using the provided Bonjour Browser we can easily find our service:


// Create the Bonjour Browser (looking for http)
// See more Bonjour Types here: https://developer.apple.com/library/mac/qa/qa1312/_index.html
httpBonjourBrowser = Ti.Network.createBonjourBrowser({
    serviceType : '_http._tcp',
    domain : 'local'
});

// Handle updated services
httpBonjourBrowser.addEventListener('updatedservices', function(e){
    _.each(e.services, function(service, i){
        // Look for our "Shockoe" service
        if(service && service.name && service.name == 'Shockoe'){
            // We have found the Desktop Application!

            // Stop the search
            httpBonjourBrowser.stopSearch();

            // Resolve the service (required to get socket information)
            service.resolve();

            // Do something with the service
	});
});

// Start searching
httpBonjourBrowser.search();

Perfect! We can find our service in iOS! What about Android? Uh oh. There is nothing provided in the Titanium docs for utilizing zeroconf on Android. Unfortunately the answer is going with a native module. I wasn’t able to find a free solution that is up to date, so I had to write my own.

Because we are just doing discovery I have not packaged my module and made it public. This is a very limited implementation that will only work for our specific use case. I share code snipets below, as well as my notes, which should help anyone trying to implement this.

Network Discovery on Android is challenging for several reasons. Apple likes to present Bonjour like it’s a unique technology, but it’s not. This make it difficult to search for. Instead of just searching for “android bonjour” it may be more useful to search for “android network discovery”, “android mdns”, “android zeroconf”, or “android avahi”. Once you realize what you need to be searching for it becomes clear there is a solution! The first solution to explore is the built in Android NsdManager. Google even offers a decent tutorial on how to use it. Here’s what I did with it:


/**
 * Uses zeroconf to discover specific http servers by name
 * 
 * This file was auto-generated by the Titanium Module SDK helper for Android
 * Appcelerator Titanium Mobile
 * Copyright (c) 2009-2010 by Appcelerator, Inc. All Rights Reserved.
 * Licensed under the terms of the Apache Public License
 * Please see the LICENSE included with this distribution for details.
 *
 */
package com.shockoe.zeroconf;

import java.util.HashMap;

import org.appcelerator.kroll.KrollDict;
import org.appcelerator.kroll.KrollProxy;
import org.appcelerator.kroll.annotations.Kroll;
import org.appcelerator.titanium.TiC;
import org.appcelerator.titanium.util.Log;
import org.appcelerator.titanium.util.TiConfig;
import org.appcelerator.titanium.util.TiConvert;
import org.appcelerator.titanium.TiApplication;

import android.content.Context;

// Network Service Discovery Packages
import android.net.nsd.NsdServiceInfo;
import android.net.nsd.NsdManager;

import android.app.Activity;


// KrollProxy
@Kroll.proxy(creatableInModule=ZeroconfModule.class)
public class HTTPServiceLocatorProxy extends KrollProxy
{
    // Standard Debugging variables
    private static final String LCAT = "HTTPServiceLocatorProxy";
    private static final boolean DBG = TiConfig.LOGD;

    // NsdManager and its listeners
    private NsdManager mNsdManager;
    NsdManager.ResolveListener mResolveListener;
    NsdManager.DiscoveryListener mDiscoveryListener;
    NsdManager.RegistrationListener mRegistrationListener;

    // The service type we will be searching for
    private String _serviceType;

    // The name of the service we will be searching for
    private String _serviceName;

    NsdServiceInfo mService;

    // Constructor
    public HTTPServiceLocatorProxy()
    {
        super();

        // Store a reference to the NSD Service of the current activity.
        TiApplication appContext = TiApplication.getInstance();
        Activity activity = appContext.getCurrentActivity();
        mNsdManager = (NsdManager) activity.getSystemService(Context.NSD_SERVICE);
    }

    // Handle creation options
    @Override
    public void handleCreationDict(KrollDict options)
    {
        super.handleCreationDict(options);

        if(options.containsKey("serviceType")){
            _serviceType = options.get("serviceType").toString();
        } else {
            _serviceType = "_http._tcp";
        }

        if(options.containsKey("serviceName")){
            _serviceName = options.get("serviceName").toString();
        } else{
            _serviceName = "ShockoeService";
        }
    }

    // Accessors
    @Kroll.getProperty @Kroll.method
    public String getServiceType(){
        return _serviceType;
    }

    @Kroll.setProperty @Kroll.method
    public void setServiceType(String serviceType){
        _serviceType = serviceType;
    }

    @Kroll.getProperty @Kroll.method
    public String getServiceName(){
        return _serviceName;
    }

    @Kroll.setProperty @Kroll.method
    public void setServiceName(String serviceName){
        _serviceName = serviceName;
    }

    // Methods
    @Kroll.method
    public void search(){
        // Instantiate a new DiscoveryListener
        mDiscoveryListener = new NsdManager.DiscoveryListener() {

            @Override
            public void onDiscoveryStarted(String regType) {
                Log.d(LCAT, "Service discovery started");
            }

            @Override
            public void onServiceFound(NsdServiceInfo service) {
                // A service was found!  Do something with it.
                Log.d(LCAT, "Service discovery success" + service);

                if(service.getServiceName().equals(_serviceName)){
                    // This is the service we are looking for, resolve it.
                    mNsdManager.resolveService(service, mResolveListener);
                }
            }

            @Override
            public void onServiceLost(NsdServiceInfo service) {
                Log.e(LCAT, "service lost" + service);
            }

            @Override
            public void onDiscoveryStopped(String serviceType) {
                Log.d(LCAT, "Discovery stopped: " + serviceType);
            }

            @Override
            public void onStartDiscoveryFailed(String serviceType, int errorCode) {
                Log.e(LCAT, "Discovery failed: Error code:" + errorCode);
            }

            @Override
            public void onStopDiscoveryFailed(String serviceType, int errorCode) {
                Log.e(LCAT, "Discovery failed: Error code:" + errorCode);
                mNsdManager.stopServiceDiscovery(this);
            }
        };

        // Instantiate a new ResolveListener
        mResolveListener = new NsdManager.ResolveListener() {

            @Override
            public void onResolveFailed(NsdServiceInfo service, int errorCode) {
                Log.e(LCAT, "Resolve failed" + errorCode);
            }

            @Override
            public void onServiceResolved(NsdServiceInfo service) {
                Log.d(LCAT, "Resolve Succeeded. " + service);

                // If JavaScript land is listening, report back our findings
                if(hasListeners("servicefound")){
                    HashMap event = new HashMap();
                    event.put("name", service.getServiceName());
                    event.put("type", service.getServiceType());
                    event.put("host", service.getHost().toString().substring(1)); //Host returns has a garbage leading "/"
                    event.put("port", service.getPort());
                    fireEvent("servicefound", event);
                }
            }
        };

        // Start discovering services
        mNsdManager.discoverServices(_serviceType, NsdManager.PROTOCOL_DNS_SD, mDiscoveryListener);
    }

    @Kroll.method
    public void stopSearch(){
        if(mDiscoveryListener != null){
            Log.d(LCAT, "Discovery Listener IS initialized!");
            mNsdManager.stopServiceDiscovery(mDiscoveryListener);
            mDiscoveryListener = null;
        } else{
            Log.d(LCAT, "Discovery Listener not initialized!");
        }
    }

}

This exposes two methods, one that starts the search and one that stops it. The idea is to use it like this:


httpServiceLocator = require('com.shockoe.zeroconf').createHTTPServiceLocator({
    serviceType : '_http._tcp.',
    serviceName : 'ShockoeService'
});

// Handle our service being found
httpServiceLocator.addEventListener('servicefound', function(e){
    // Stop the search
    httpServiceLocator.stopSearch();

    // We have found our service, do something with it

    // Build url based off of service information
    var baseUrl = 'http://'+e.host+':'+e.port;
});

httpServiceLocator.search();

Note that this is different than the built in BonjourBrowser. For our use we wanted to find a single service, retrieve the url, and stop searching once we have found it.

This code does exactly that. Unfortunately it has some issues. First of all, the NsdManager was added in Android 4.1. If you need to support devices lower than that you will need a different library. Second, NsdManager has issues finding services. In my testing I had three services broadcasting over zeroconf on my network. One was the service I was looking for running on a Windows 8 laptop, another was a test service in NodeJS using the mdns package from npm running on a Macbook, and finally a Mac Mini broadcasting our internal CI system portal. Using NsdManager on devices running Android 4.4 or lower I could only find the node server. I tried many scenarios and it was the only server I could find. On Android 5.0.1 or higher I could find all the services without an issue. If your user is on Android 5.0.1 or higher I would recommend the NsdManager approach as seen above. If your user is on Android 4.x or lower I would recommend using another library, jmdns.


/**
 * Uses zeroconf to discover specific http servers by name
 * 
 * This file was auto-generated by the Titanium Module SDK helper for Android
 * Appcelerator Titanium Mobile
 * Copyright (c) 2009-2010 by Appcelerator, Inc. All Rights Reserved.
 * Licensed under the terms of the Apache Public License
 * Please see the LICENSE included with this distribution for details.
 *
 */
package com.shockoe.zeroconf;

import java.util.HashMap;

import org.appcelerator.kroll.KrollDict;
import org.appcelerator.kroll.KrollProxy;
import org.appcelerator.kroll.annotations.Kroll;
import org.appcelerator.titanium.TiC;
import org.appcelerator.titanium.util.Log;
import org.appcelerator.titanium.util.TiConfig;
import org.appcelerator.titanium.util.TiConvert;
import org.appcelerator.titanium.TiApplication;

import android.content.Context;

// Network Service Discovery Packages
import java.io.IOException;

import javax.jmdns.JmDNS;
import javax.jmdns.ServiceEvent;
import javax.jmdns.ServiceListener;

import android.net.wifi.*;
import java.net.InetAddress;
import java.net.UnknownHostException;

import android.app.Activity;


// KrollProxy
@Kroll.proxy(creatableInModule=ZeroconfModule.class)
public class JmdnsHTTPServiceLocatorProxy extends KrollProxy
{
    // Standard Debugging variables
    private static final String LCAT = "JmdnsHTTPServiceLocatorProxy";
    private static final boolean DBG = TiConfig.LOGD;

    // Jmdns and its listeners
    private JmDNS jmdns = null;
    private ServiceListener listener = null;

    WifiManager wifi;
    WifiManager.MulticastLock lock;

    // The service type we will be searching for
    private String _serviceType;

    // The name of the service we will be searching for
    private String _serviceName;

    // Constructor
    public JmdnsHTTPServiceLocatorProxy()
    {
        super();

        // Store a reference to the NSD Service of the current activity.
        TiApplication appContext = TiApplication.getInstance();
        Activity activity = appContext.getCurrentActivity();
        wifi = (android.net.wifi.WifiManager) activity.getSystemService(Context.WIFI_SERVICE);
    }

    // Handle creation options
    @Override
    public void handleCreationDict(KrollDict options)
    {
        super.handleCreationDict(options);

        if(options.containsKey("serviceType")){
            _serviceType = options.get("serviceType").toString() + ".local.";
        } else {
            _serviceType = "_http._tcp" + ".local.";
        }

        if(options.containsKey("serviceName")){
            _serviceName = options.get("serviceName").toString();
        } else{
            _serviceName = "ShockoeService";
        }
    }

    // Accessors
    @Kroll.getProperty @Kroll.method
    public String getServiceType(){
        return _serviceType;
    }

    @Kroll.setProperty @Kroll.method
    public void setServiceType(String serviceType){
        _serviceType = serviceType + ".local.";
    }

    @Kroll.getProperty @Kroll.method
    public String getServiceName(){
        return _serviceName;
    }

    @Kroll.setProperty @Kroll.method
    public void setServiceName(String serviceName){
        _serviceName = serviceName;
    }

    // Methods
    @Kroll.method
    public void search(){
        WifiInfo wifiinfo = wifi.getConnectionInfo();
        int intaddr = wifiinfo.getIpAddress();

        byte[] byteaddr = new byte[] { (byte) (intaddr & 0xff), (byte) (intaddr >> 8 & 0xff), (byte) (intaddr >> 16 & 0xff), (byte) (intaddr >> 24 & 0xff) };
        try{
            InetAddress addr=InetAddress.getByAddress(byteaddr);
            lock = wifi.createMulticastLock("com.shockoe.zeroconf");
            lock.setReferenceCounted(true);
            lock.acquire();
                
            try {
                Log.d(LCAT, "_serviceType: "+_serviceType);
                jmdns = JmDNS.create(addr); 
                
                jmdns.addServiceListener(_serviceType, listener = new ServiceListener() {

                    @Override
                    public void serviceResolved(ServiceEvent ev) {
                        if(ev.getName().equals(_serviceName)){

                            // If JavaScript land is listening, report back our findings
                            if(hasListeners("servicefound")){
                                HashMap event = new HashMap();
                                event.put("name", ev.getName());
                                event.put("type", ev.getType());
                                event.put("host", ev.getInfo().getInetAddresses()[0].getHostAddress());
                                event.put("port", ev.getInfo().getPort());
                                fireEvent("servicefound", event);
                            }
                        }
                    }

                    @Override
                    public void serviceRemoved(ServiceEvent ev) {
                        Log.d(LCAT, "Service removed: " + ev.getName());
                    }

                    @Override
                    public void serviceAdded(ServiceEvent event) {
                        Log.d(LCAT, "service added: "+event);
                        if(event.getName().equals(_serviceName)){
                            Log.d(LCAT, "this is the service we are looking for!");
                            // This is the service we are looking for, resolve it.
                            jmdns.requestServiceInfo(event.getType(), event.getName(), 1);  
                        }
                    }
                });
            } catch (IOException e) {
                e.printStackTrace();
                return;
            }
        } catch(UnknownHostException e){
            Log.d(LCAT, "UNKNOWN HOST EXCEPTION");
        }


        
    }

    @Kroll.method
    public void stopSearch(){
        if(jmdns != null){
            Log.d(LCAT, "Stopping search!");
            lock.release();
            jmdns.removeServiceListener(_serviceType, listener);
            try{
                jmdns.close();
                jmdns = null;
            } catch(IOException e){
                Log.e(LCAT, "Error closing jmdns!");
            }
        } else {
            Log.d(LCAT, "Search not active!");
        }
    }
}

The module is used in the exact same way, but relies on jmdns instead.


httpServiceLocator = require('com.shockoe.zeroconf').createJmdnsHTTPServiceLocator({
    serviceType : '_http._tcp.',
    serviceName : 'ShockoeService'
});

// Handle our service being found
httpServiceLocator.addEventListener('servicefound', function(e){
    // Stop the search
    httpServiceLocator.stopSearch();

    // We have found our service, do something with it

    // Build url based off of service information
    var baseUrl = 'http://'+e.host+':'+e.port;
});

httpServiceLocator.search();

Jmdns is a little buggy and will not find the service every time. Use NsdManager when you can, but verify that it works for your usage.

These examples have only been for discovering the service and don’t even handle communicating over a socket directly through zeroconf like the BonjourBrowser does. I have also not included code for broadcasting a service on the mobile device itself. Hopefully this code points you in the right direction if those are things you need to do from an Android device with Appcelerator Titanium!

Now that I’ve had a chance to work with zeroconf I love what it brings to the table. It’s great to have devices just find each other on a network with no input from the user. Unfortunately the way Bonjour is presented makes it difficult to find information on how to implement zeroconf on devices that Apple does not directly support.

Node.js – Storing data with MongoDB

Node.js – Storing data with MongoDB

The last few weeks we have focused on web service design using Node.js with Express. So far we have covered getting started with express, using EJS to create web pages, and creating a push notification server. Today we will go over how to save data on your node project by using MongoDB.

This post will assume that you have MongoDB installed on your machine already. Check out the MongoDB Installation page if you do not have it setup.

First, let’s go over MongoDB a bit. MongoDB is a document oriented database. This allows you to just shove whatever JSON you want into it. You don’t have to declare a table structure like you do in SQL. You don’t even need to declare your collections at all. If you try to use a collection that doesn’t exist it just gets made for you immediately. Documents are stored in collections that you define, but documents within a collection don’t have to be similar at all. MongoDB also features an amazing query system. Imagine we want to store information about fruit. We could do the following:

db.fruit.insert({name : "apple", color : ["red", "yellow"], source : "tree"})

We now have a fruit collection containing one fruit: an apple. Let’s add a few more:

db.fruit.insert({name : "strawberry", color : ["red"], source : "bush"})
db.fruit.insert({name : "orange", color : ["orange"], source : "tree"})

Now we have a strawberry and an orange in our fruit collection. To get all fruits in our collection that come from trees we would do:

db.fruit.find({source : "tree"})

If we wanted to find all fruits that are red we would do:

db.fruit.find({color : "red"})

And to find all fruits that are yellow or orange we would do:

db.fruit.find({color : {$in : ["yellow", "orange"]}})

Now apples can be green too. Let’s modify our apple entry to reflect that:

db.fruit.update({name : "apple"}, {$push : {color : "green"}})

There are a few interesting things to be aware of when updating a document. By default, mongodb will only update the first entry it finds. You can set the multi option to force it to update every entry that matches the query. Also, note the $push operator I have included in the update object. That is telling mongo it will be adding an entry to an array on the document. If you left off push and just did:

db.fruit.update({name : "apple"}, {color : "green"})

You would be very disappointed. Setting no update operator will replace the document completely:

{
    "color" : "green"
}

So what if you wanted to update a field that isn’t an array? You use the $set update operator:

db.fruit.update({name : "apple"}, {$set : {name : "pear"}}

MongoDB is very powerful and an excellent document store, but it lacks the join power that SQL brings to the table. You can join documents together by their _id field using MonoDB’s populate, but it’s not nearly as fast as what you can do with a few SQL joins. I encourage you to explore MongoDB as an option for your databases, but it’s important to approach your database design differently with MongoDB than with SQL. Be sure to check out the documentation for all you can do with MongoDB.

So how to we start using Mongo with Node? There are several libraries available on npm for connecting to Mongo, but I will be focusing on Mongoskin and Mongoose.

Let’s go ahead and get Mongoskin out of the way. Mongoskin is what you want if you just want to run basic MongoDB commands and nothing more. To get started add Mongoskin as a dependency:

npm install mongoskin --save

And then in node:

var mongo = require('mongoskin');
var db = mongo.db("mongodb://localhost:27017/integration_tests", {native_parser:true});
db.collection('fruit).find({name : "orange"}).toArray(function(err, fruits){
    console.log("I have "+fruits.length+" fruits in my collection:");
    for(var i=0; i<fruits.length; i++){
        console.log(fruit.name);
    }
});

Mongoskin is very easy to use and most MongoDB commands are supported. It has its issues, but if you want something simple to connect to a mongo database look no further. If you want the best MongoDB experience you can find with Node, keep reading!

On to Mongoose. As they say on their website: “Mongoose provides a straight-forward, schema-based solution to modeling your application data and includes built-in type casting, validation, query building, business logic hooks and more, out of the box.” Mongoose is a delight. To get started add Mongoose as a dependency:

npm install mongoose --save

It’s nice that MongoDB doesn’t need to have schemas, but it’s great that Mongoose gives them to you. Let’s put together a schema for our fruit collection:

var mongoose = require('mongoose');

var FruitSchema = new mongoose.Schema({
    name : String,
    color : [String],
    source : {
        type : String,
        default : 'tree'
    }
});

FruitSchema.virtual('numberOfColors').get(function(){
    return this.color.length;
});

mongoose.model('Fruit', EventSchema);

Most of the schema above should be straight forward. Name must be a string or Mongoose will remove the property from the object before using it as a model. Color must be an array of strings. Source must be a string, and if it’s not present Mongoose will automatically set it to “tree”. Default values will not get stored in the actual database, but they will be applied when Mongoose pulls them out. The virtual “numberOfColors” will add a property automatically. Virtuals are great for properties that can be calculated based on the existing data on the model.

To use the fruits model:

mongoose.model('Fruit').find({name : "orange"}, function(err, fruits){
    console.log("I have "+fruits.length+" fruits in my collection:");
    for(var i=0; i<fruits.length; i++){
        console.log(fruit.name + " comes in " + fruit.numberOfColors + " colors");
    }
});

The examples above barely scrape the surface of what you can do with MongoDB and Mongoose. Do yourself a favor and explore the possibilities of a document oriented database backed by MongoDB.

Node.js – A JavaScript Developer’s Dream

Node.js – A JavaScript Developer’s Dream

Here at Shockoe, we are happiest when working with JavaScript. There are plenty of projects that call for other languages, like Objective-C, Java, or PHP, but the goal is to work with JavaScript whenever possible. For a decade I thought of JavaScript as this insecure toy language used to do miscellaneous effects on the web. I gave JavaScript another look when AJAX came into the vogue but again dismissed it as a toy language with no real use. Then I started working at Shockoe, where I was very surprised to learn that JavaScript could be used for writing mobile applications.

Despite working for a company that primarily deals with mobile applications, I consider myself a web developer. I’ve been using PHP for a very long time and mostly just tolerated its inconsistencies and occasional strange syntax. PHP was just the way you did server side code for the web. Then I discovered Node.js. Now the goal is to write everything in JavaScript.

Node.js is advertised as being “for easily building fast, scalable network applications.” By default, Node.js performs very little I/O so blocking is not a concern. There’s an absurd amount of Node.js modules available from Node Package Manager (npm) that allow you to connect to almost anything imaginable. Most, if not all, modules for connecting to I/O provide async methods so that blocking remains of little to no concern.

To get started with Node, head on over to nodejs.org There are builds available for Windows, OSX, and Linux. Then head over to npmjs.com to see what modules Node Package Manager has to offer. There’s web frameworks, build harnesses, database drivers, and much more. Any developer who loves JavaScript owes it to themselves to explore the world of Node.js.

I’m at the point where I use Node.js for anything from giant web projects to minor fun tasks. At Shockoe, we have taken an initiative to improve our health. One concern is that we spend all day looking at monitors which puts a lot of strain on our eyes. To combat this we have implemented a system known as the twenty twenty twenty rule. Every twenty minutes we take a twenty second break while looking at something twenty feet away. To alert everyone of the time we have a node server, running on our continuous integration machine in the corner, that turns a hue light purple for twenty seconds every twenty minutes. It’s enough to catch everyone’s attention without being extremely interrupting. This task could have been accomplished with cron, but I don’t want to schedule cron jobs. The goal is to write JavaScript.

In the upcoming weeks we at Shockoe will be expanding upon this post and, step by step, creating a project using Node.js.

Digest Access Authentication with Appcelerator Titanium HTTPClient

Digest Access Authentication with Appcelerator Titanium HTTPClient

I’ve been working with web technologies for over a decade and never had to touch Digest Access Authentication.  All of the services I had worked with had other solutions for authentication.  A few months back that all changed.  I was finishing up a mobile project, to be used for internal purposes for the client, when suddenly the API requirements changed.  They had implemented Digest Access Authentication with MD5.  Uh-oh.

What is Digest Authentication?

Let’s start small; I certainly had to.  Digest Access Authentication is a method for validating a user and granting them some access over HTTP.  It involves making a request, being presented with a challenge, answering that challenge with another request, and finally getting the resource originally requested.  It works like this:

  1. Client sends request to the server
  2. Server responds with a nonce (number to be used once) with a status of 401
  3. Client makes another request to the server providing identification information
  4. Server evaluates whether the user is valid and if they are who they say are
  5. Server responds with desired resource

How do we do this in Titanium?

The idea is to send out requests like normal, check for a 401 status code, and respond to the presented challenge if applicable.  That is simple enough:


//Create the HTTPClient
xhr = Titanium.Network.createHTTPClient();

xhr.onload = function(){
	//Process the response
}

xhr.onerror = function(){
	//Process an error
	if(this.status == 401){
		//Respond to challenge
	}
}

When I was originally tackling this problem I found a very helpful example on github by rollsroyc3: https://gist.github.com/rollsroyc3/6869880  The majority of the following code is from that example, but I made a few changes.

Before adding any code to actually handle the challenge, let’s take a step back.  Assuming you have multiple HTTPClients, like I did, every HTTPClient would need to be rewritten.  Instead, let’s encourage code reuse and turn this into a commonJS lib that wraps the HTTPClient.  Then we can have one HTTPClient that does Digest Authentication when it encounters a 401 status code and acts normally with all others:

(I use underscore.js a bit here, and you should too)


var timeout = 2000;

 
 /**
  * The authentication magic.
  *
  * Breaks down a 401 request from a digest authentication endpoint and generates a response
  * 
  * @param  {Response Headers} h : The response headers
  * @return {Request Headers} : The request headers to match the response
  */
function parseAuthenticationResponse(h) {
	var auth = {
		headers : {}
	};
	var scre = /^\w+/;
	var scheme = scre.exec(h);
	auth.scheme = scheme[0];
 
	var nvre = /(\w+)=['"]([^'"]+)['"]/g;
	var pairs = h.match(nvre);
 
	var vre = /(\w+)=['"]([^'"]+)['"]/;
	var i = 0;
	for (; i < pairs.length; i++) {
		var v = vre.exec(pairs[i]);
		if (v) {
			auth.headers[v[1]] = v[2];
		}
	}
	return auth;
}

/**
 * Generates a unique token for a CNONCE value using the device ID and current time
 * @return {String} : Unique token for a CNONCE value
 */
function generateUniqueCNONCE(){
	return new Date().getTime() + Ti.Platform.id;
}
 

/**
 * Performs the secondary request with our response to the challenge
 * @param  {String}   requestType : HTTP verb
 * @param  {String}   url         : The location of the desired resource
 * @param  {Object}   postData    : Any data that needs to be sent with the request
 * @param  {String}   header      : The Authorization Header
 * @param  {Function} onload      : Callback if successful
 * @param  {Function} onerror     : Callback if failure
 * @return {void}             
 */
exports.httpClient = function(requestType, url, postData, header, onload, onerror) {
	//Create the client
	var xhr = Titanium.Network.createHTTPClient();

	//We've already tried to authorize at this point, send the appropriate callbacks
	xhr.onload = function() {
		if (_.isFunction(onload)){
			onload((this.responseText ? JSON.parse(this.responseText) : null), xhr);
		}
	};
 
	xhr.onerror = function(e) {
		if(_.isFunction(onerror)){
			onerror(xhr, e);
		}
	};

	xhr.setTimeout(timeout);

	//Perform the request
	xhr.open(requestType, url);
 
 	//Set the header
	header && xhr.setRequestHeader("Authorization", header);
 
	if ((requestType == 'POST' || requestType == 'PUT' || requestType == 'DELETE') && postData) {
		//Set the content type
		xhr.setRequestHeader('Content-Type', 'application/json; charset=utf-8');
		//Stringify the post data, to ensure it's not url encoded
		xhr.send(JSON.stringify(postData));
	} else {
		xhr.send();
	}
};

/**
 * Performs the initial request
 * @param  {String}   requestType : HTTP verb
 * @param  {String}   url         : The location of the desired resource
 * @param  {Object}   postData    : Any data that needs to be sent with the request
 * @param  {Function} onload      : Callback if successful
 * @param  {Function} onerror     : Callback if failure
 * @param  {Object}   user        : Object containing username nad password for auth
 * @return {void}             
 */
exports.httpClientDigest = function(requestType, url, postData, onload, onerror, user) {
	//Create the client
	var xhr = Titanium.Network.createHTTPClient();

	//If all goes well we don't need to do anything!
	xhr.onload = function() {
		if(_.isFunction(onload)){
			onload((this.responseText ? JSON.parse(this.responseText) : null) || this.responseText, xhr);
		}
	};
 
 	//If there's an error, we need to check for an authentication challenge
	xhr.onerror = function(e){
		if (this.status == 401) {
      		//401 error, time to authenticate
			var headers = this.getResponseHeaders();
			var tokensObj = parseAuthenticationResponse(headers['Www-Authenticate']);
 
 			//Build the headers
			tokensObj.headers.cnonce = generateUniqueCNONCE();
			tokensObj.headers.nc = '00000001';
			tokensObj.headers.algorithm = 'MD5';
			tokensObj.headers.method = requestType;
			tokensObj.headers.domain = url;
 
			var HA1 = Ti.Utils.md5HexDigest(user.username + ':' + tokensObj.headers.realm + ':' + user.password);
			var HA2 = Ti.Utils.md5HexDigest(tokensObj.headers.method + ':' + tokensObj.headers.domain);
			var authResponse = Ti.Utils.md5HexDigest(HA1 + ':' + tokensObj.headers.nonce + ':' + tokensObj.headers.nc + ':' + tokensObj.headers.cnonce + ':' + tokensObj.headers.qop + ':' + HA2);
			var responseContentHeader = 'Digest username="' + user.username + '"' + ', realm="' + tokensObj.headers.realm + '"' + ', nonce="' + tokensObj.headers.nonce + '"' + ', uri="' + tokensObj.headers.domain + '"' + ', algorithm="' + tokensObj.headers.algorithm + '"' + ', response="' + authResponse + '"' + ', qop="' + tokensObj.headers.qop + '"' + ', nc=' + tokensObj.headers.nc + ', cnonce="' + tokensObj.headers.cnonce + '"';
      
      		//Respond to the authentication and request the resource again
			exports.httpClient(requestType, url, postData, responseContentHeader, onload, onerror);
		}
		else{
			//Some other error, let the callback handle it
			if(_.isFunction(onerror)){
				onerror(xhr, e);
			}
		}
	};

	xhr.setTimeout = timeout;

	//Perform the request
	xhr.open(requestType, url);
	
 
 	//Evaluate whether there is data to send
	if ((requestType == 'POST' || requestType == 'PUT' || requestType == 'DELETE') && postData) {
		//Set the content type
		xhr.setRequestHeader('Content-Type', 'application/json; charset=utf-8');
		//Stringify the post data, to ensure it's not url encoded
		xhr.send(JSON.stringify(postData));
	}
	else {
		xhr.send();
	}
};

We can then require this in and use it:


var httpDigest = require('httpDigest');

//Data to send
var data = {
	test: 'string'
};

//Connect through HTTP Client with Digest
httpDigest.httpClientDigest('POST', address, data, function(response, client){
	//Do something on success
},function(client, e){
	//Do something on failure
}, {username: 'username', password: 'password'});

Improvements

This is just one example of how it can be accomplished.  Your needs may be different.  There are many more options available to the HTTPClient that are not exposed (like headers).  This was a quick and dirty solution to a last minute problem.

If I needed the library today for a new project I would modify it to make use of Backbone events.  Imagine different components being notified when a new request has been made, when that request has been met with Digest Access Authentication, and when the response is finally successful!  Evented networks are both a blessing and a curse.  Allowing various controllers to listen for updates to data is amazing, but if you’re not careful with cleanup then you’re begging for memory leaks.