Building a Real-time, Multi-platform App in 3 Months

Joe Crick by Joe Crick

Building a Real-time, Multi-platform App in 3 Months

Joe Crick

Our team developed a real-time iOS, Android, and web application to connect truckers and shippers in 3 months with 2 developers. Here’s how we did it.

posted in Consulting ,Development on February 14, 2017 by Joe Crick

Bitovi worked with HaulHound, a logistics technology company, to design and develop a system connecting truckers and shippers. The system we built supports real-time data, an iOS and Android app for truckers, and a web application for shippers.

Yet we completed the development in just 3 months, with a team of two developers!

We're very proud of our work together with Haulhound. This post will walk through how we built the entire app.

We'll cover:

  • What's in the app
  • How we developed the app
    • Our stack
    • Timeline
    • Bootstrapping
  • An overview of the key features
    • Real-time
    • Mapping
    • Mobile app build

For more background on the project, check out our Haulhound case study or view the apps on the the web, App Store, or Google Play


What we built

The Haulhound app contains three parts:

image08.png

The phone app is intended for truckers to post their route and extra truck capacity.

hh1.jpeghh2.jpeg

The desktop app lists the loads and allows shippers to buy empty truck capacity directly.

haulhound-anim-1-2x.png

How we Built the App

We knew time was limited, so we designed an aggressive timeline with as much parallel work as possible.  Here's roughly what that looked like.

Process

Our designer began with user flows and low fidelity mockups while our developers were laying the technical foundation. Each sprint, after a low fidelity mockup was approved, the devs would move forward with implementing that UI.

As designs got higher fidelity, refinements were made to the UI. 

haulhound-evolution-md.jpg

Tech stack

One of the reasons HaulHound chose Bitovi was our OS stack. Nowadays, you can built a great app with a modern stack and a great team, but Haulhound enjoyed several key advantages:
  • Direct support from Bitovi's OS team - delays are avoided because framework issues are resolved in hours or days vs weeks or months
  • Bitovi devs know our own tools extremely well because we all contribute to them

We used the following tech to build the application:

haulhound-stack.gif

Bootstrapping

We were able to make rapid progress right from the start. This was one of the keys to the project’s success. In many cases, bootstrapping a full app can take several weeks and require lots of manual decisions and configuration. On HaulHound, bootstrapping the app took only two days.

haulhound-timeframe.gif

The reason for the rapid bootstrapping is that DoneJS comes with everything needed on the UI, so there is no assembly required. We ran generators that bootstrapped an app with a build, Cordova integration, testing, basic routing, components, and models. We just had to hook it up to the relevant APIs.

Real Time

To facilitate real-time data, we created Feathers services. The majority of the services were built in a matter of weeks. This snippet shows backend code that sets up Feathers services.

const service = require('feathers-mongoose');
const hooks = require('./hooks');
const mongoose = require('mongoose');
const Schema = require('../../validation/schema');
const carrierSchema = require('../../schemas/carrier');

const Model = mongoose.model('carrier', carrierSchema);

module.exports = function() {
  const app = this;
  const paginate = app.get('paginate');

  // Initialize our service with any options it requires
  app.use('/carriers', service({ Model, paginate }));

  // Get our initialize service to that we can bind hooks
  const carrierService = app.service('/carriers');

  // Set up our before hooks
  carrierService.before(hooks.before);

  // Set up our after hooks
  carrierService.after(hooks.after);
};

Using fixtures (fake services that simulate a server response with mock data) we were able to develop with the UI and BE in parallel.

import fixture from 'can-fixture';
import config from '../../configuration';

const store = fixture.store([{
  _id: 0,
  description: 'First item'
}, {
  _id: 1,
  description: 'Second item'
}]);

fixture({
  [`GET ${config.api}/users`]: store.findAll,
  [`GET ${config.api}/users/{_id}`]: store.findOne,
  [`POST ${config.api}/users`]: store.create,
  [`PUT ${config.api}/users/{_id}`]: store.update,
  [`PATCH ${config.api}/users/{_id}`]: store.update,
  [`DELETE ${config.api}/users/{_id}`]: store.destroy
});

export default store;

To connect the UI to the backend, we setup and configured can-connect-feathers:

import Feathers from 'can-connect-feathers';
import config from './configuration';

const socketio = false;
// System.env === 'testing' ? false : { transports: ['websocket'] };

const feathers = new Feathers({
  url: config.api,
  idProp: '_id',
  socketio,
  storage: window.localStorage
});

if(!socketio) {
  feathers.io = {
    once() {},
    on() {},
    off() {},
    emit() {}
  };
}

export default feathers;

Then, consumed the Feathers services in a DoneJS model:

const service = feathers.rest('/locations');

export const locationConnection = connect(behaviors, {
  url: {
     getListData: service.find,
     getData: service.get,
     createData: service.create,
     updateData: service.patch,
     destroyData: service.remove
  },
  idProp: '_id',
  Map: Location,
  List: Location.List,
  name: 'location'
});

That's it. Once these layers were configured, real-time data was able to flow into the application.

HERE Maps Integration

Shipping and routing data changes all the time, and truckers need immediate updates.

We already had real-time support in place via the Feathers/can-connect integration shown above, but we needed something to manage location and routing data. For this, HaulHound chose to work with Nokia HERE maps. HERE maps has a public JavaScript API.

export function calculateRoute(pickup, delivery) {
  const router = platform.getRoutingService();
  const routeRequest = assign({}, routeRequestDefaults);
  routeRequest.waypoint0 = pickup.attr();
  routeRequest.waypoint1 = delivery.attr();

  return new Promise((resolve, reject) => {
    router.calculateRoute(routeRequest, resolve, reject);
  });

This integration allowed us to quickly create a location picker.

location-picker.gif

Building to iOS and Android apps with Cordova

Our apps were intended for use in hybrid mobile apps on iOS and Android. DoneJS Cordova support allowed us to do this very quickly. We set up the following config:

var cordovaOptions = {
  buildDir: "./build/cordova",
  id: "com.haulhound",
  name: "HaulHound",
  platforms: [ platform ],
  plugins: [
    "cordova-plugin-statusbar",
    "cordova-plugin-transport-security",
    "cordova-plugin-geolocation",
    "cordova-plugin-splashscreen@2.0"
  ],
  index: __dirname + "/mobile.production.html",
  glob: [
    "resources/**/*",
    "node_modules/steal/steal.production.js",
    "node_modules/bootstrap/fonts/**/*",
    "dist/src/styles/img/**/*",
    "dist/src/styles/fonts/**/*"
  ]
};

var stealCordova = require("steal-cordova")(cordovaOptions);
var cordovaBuild;

if(buildIos || buildAndroid) {
  cordovaBuild = buildPromise
    .then(stealCordova.build)
    .then(function overwriteConfigXml() {
      return fs.copy('resources/' + platform + '.config.xml', 'build/cordova/config.xml');
    })
    .then(stealCordova[platform].emulate);
} else {
  buildPromise.then(function copyCDNAssets() {
    return Promise.all([
      fs.copy('desktop.production.html', 'dist/index.html'),
      fs.copy('resources/', 'dist/resources/')
    ]);
  });
}

installed the required smart device simulators, like ios-sim, and ran the build:

Pasted image at 2017_02_13 11_44 AM.png

After the Cordova build finished, we could debug the application using ios-sim, and Safari’s developer tools:

image05.png

Using Cordova let us stick with one codebase that served three environments: Android, iOS, and the web. The phone apps were indistinguishable from native apps, yet we were able to complete the project in one-third the time it would have taken to build platform-specific apps.

Moving Forward

In just three months, our team of two developers built a production-worthy, real-time, multi-platform app. Leveraging DoneJS, Steal, and Feathers gave us the ability to bootstrap quickly, and built-in support for real-time and Cordova builds helped us focus most of our time on business logic.

Haulhound's project is an ideal example of a client engagement for Bitovi. Thanks to amazing advances in open source tools - both our own and others - it's incredible how such a complex system can be implemented in such a short amount of time with only two engineers!

The project was even featured on Discovery Channel!

Special thanks to Bitovi engineers David Luecke, Kevin Phillips, and John Gardner (who filled in admirably after Kevin left for paternity leave) for all their hard work to make this project a success!

 

 

Our work with HaulHound is ongoing. We're excited to build on this foundation with new features and integrations.

Create better web applications. We’ll help. Let’s work together.