konstantinbueschel
12/1/2013 - 5:40 PM

This is a little CommonJS library I put together for adding Mixpanel tracking to an iOS and Android Appcelerator Titanium app I built. YMMV

This is a little CommonJS library I put together for adding Mixpanel tracking to an iOS and Android Appcelerator Titanium app I built. YMMV. I'm sharing it since I hadn't seen anything similar, and Mixpanel rocks. There are no native dependencies. All Mixpanel API calls are made by using the HTTP specification (https://mixpanel.com/docs/api-documentation/http-specification-insert-data) when the network is available. Event tracking, people properties and aliasing are supported.

It pulls the Mixpanel token from tiapp.xml: your-token-here

// The currentUser lib stores a model representing the logged in user, and other login state information
var currentUser = require('currentUser');
// Logger is a Ti.API.info() wrapper with a bit more flexibility
var logger = require('logger');

var messageQueue = []; // put this into local storage at some point to retain recent offline events before crashes
var messageInTransit = null;
var token = Ti.App.Properties.getString('mixpanel.token');
var systemProperties = {
  mobile_osname: Ti.Platform.osname,
  mobile_ostype: Ti.Platform.ostype,
  mobile_osversion: Ti.Platform.version,
  mobile_device_mfg: Ti.Platform.manufacturer,
  mobile_device_model: Ti.Platform.model,
  mobile_locale: Ti.Platform.locale
};
var uuid = Ti.Platform.createUUID();

exports.track = function(event, properties) {
  logger.info('mixpanel track ' + event);
  properties = properties || {};
  properties = _.extend(properties, systemProperties);
  properties.time = (new Date()).getTime();
  properties.token = token;
  properties.distinct_id = currentUser.isLoggedIn ? currentUser.user.get('email') : uuid;
  event = "(App) " + event;
  messageQueue.push({
    payload: {
      event: event,
      properties: properties
    },
    type: 'event'
  });
  sendNextMessage();
};

exports.alias = function() {
  if(currentUser.isLoggedIn) {
    logger.info('mixpanel alias');
    messageQueue.push({
      payload: {
        event: '$create_alias',
        properties: {
          "alias": currentUser.user.get('email'),
          "distinct_id": uuid,
          "time": (new Date()).getTime(),
          "token": token
        }
      },
      type: 'event'
    });
    sendNextMessage();
  }
};

exports.people = {
  set: function(properties) {
    properties = properties || {};
    logger.info('mixpanel people.set ' + properties);
    if(!currentUser.isLoggedIn) {
      console.log('mixpanel people.set skipped - user not logged in');
      return false;
    }
    properties = _.extend(properties, systemProperties);
    properties = _.extend(properties, {
      '$first_name': currentUser.user.get('first_name'),
      '$last_name': currentUser.user.get('last_name'),
      '$username': currentUser.user.get('username'),
      '$email': currentUser.user.get('email'),
    });
    var payload = {
      '$set': properties,
      '$token': token,
      '$distinct_id': currentUser.user.get('email')
    };
    messageQueue.push({
      payload: payload,
      type: 'people.set'
    });
    sendNextMessage();
  }
};

function sendMessage(message) {
  var url = '';
  if(message.type == 'event') {
    url = "http://api.mixpanel.com/track/?data=" + Titanium.Utils.base64encode(JSON.stringify(message.payload)) + "&ip=1";
  } else if(message.type == 'people.set') {
    url = "http://api.mixpanel.com/engage/?data=" + Titanium.Utils.base64encode(JSON.stringify(message.payload));
  }
  xhr = Titanium.Network.createHTTPClient({
    onload : function(e) {
      messageInTransit = false;
      messageQueue.shift();
      sendNextMessage();
    },
    onerror: function(e){
      messageInTransit = false;
      logger.info('Failed mixpanel event with status ' + this.status + ' and error : ' + JSON.stringify(e));
      setTimeout(function() {
        sendNextMessage();
      }, 2000); // wait a couple of seconds before we try this again
    },
    timeout: config.imageUploadTimeout
  });
  xhr.open('POST', url);
  
  xhr.send();
}


function sendNextMessage() {
  if(!Ti.Network.online) {
    setTimeout(sendNextMessage, 10000);
    return false;
  }
  if(messageInTransit)
    return false;
  if(messageQueue.length > 0) {
    messageInTransit = messageQueue[0]; // FIFO
    sendMessage(messageInTransit);
  }
}