Work on what you want week at Trigger.io

Last week our development team have been spending some ‘15% time’ bootlegging their own pet projects. We wanted this to be an opportunity to really road-test the platform for personal apps, and to add some more ambitious features to the platform that might have been on the ‘Someday, Maybe’ list, pushed to the side by our focussed fortnightly sprints.

Here’s a rundown of what lead engineers Connor, Tim and Antoine each chose for their own projects — and how they got on:

Antoine has been building an ambitious cloud simulator service for running and testing builds in the browser — without the need for the appropriate hardware. We’re looking at making this generally available if enough people are interested.

Would you pay $49 / month for access to this feature? Let us know. Here’s Antoine’s description:

If you’re developing for multiple platforms, you’re going to need a lot of hardware to test it out — this is something that cross-platform tools on their own don’t really solve. To reduce that upfront cost, I’m developing a way to run your applications in the cloud.

This would work alongside the existing options of running locally on your own devices or simulators: we’ll implement something like a ‘forge run cloud’ command to fire up the app in an emulator running through the browser, in the cloud.

At the moment I’m doing this by running OS X on a virtual machine, and I’ve written a chunk of software to launch the app, wrap, display and export it via VNC to the browser using noVNC – which means you can view an Android emulator from an iPad:

The difficulty is scaling enough server-side support, and overcoming the need for Mac hardware to back the iOS simulators. With a bit of tidying up though, I’m hoping we could implement this fairly soon.

Connor, back in London, has been working on other ways to make iOS development completely independent of Mac hardware:

At the moment you still can’t sign an iOS app on a Windows machine — which you need to do in order to run the app. So I’ve been working on a remote signing service for Windows and Linux machines which will sign the app and return it to the local machine to run:

When you run forge run ios on OS X, the app will build and then sign locally as normal. Now, on Windows and Linux, it will upload to our build service to sign the app and return it to the local machine. A key part of this is ensuring that your developer certificate is always safe: I’ve taken great care to ensure this is the case.

Another part of this is building the ability to run on iOS devices from non-Apple hardware, and log the output back to the terminal. There’s some tidying up to do to keep the build-and-run server calls as quick as possible, but this is a feature we should be able to implement with our next update.

Tim in our London office worked on building a simple Sokoban game from scratch, for Android, iOS and Windows Phone. Because Tim works on developing the Forge toolchain day-to-day, this was a good opportunity to really test them out as a user:

Building the app has been more about immersing myself in our user experience than building the end product — I wanted to really hit the rough spots and get a better idea of working end-to-end with the tools, particularly with some of the newer features.

I’ve deliberately worked on an app that would call on our more recent features. I’ve used native UI elements (like the native topbar), and in-app payments to unlock different worlds and levels within the game. So far, so good. There’s some UI issues to iron out, then I think some of the learnings from using the in-app payments system will fold into tweaks to the API and documentation.

If you have any suggestions of suitable projects to tackle on our next discretionary week — or thoughts on how we implement any of these — get in touch and let us know!

Announcing the Trigger.io white paper: the real deal on native + HTML5

It’s a busy week on the blog: today we’re pleased to be announcing the online publication of the Trigger.io white paper.

We’ve written this as a tight, 23-page overview of the mobile development landscape, HTML5 opportunity and how our product fits into it.

With the app market still exploding, and so many routes to entry – different platforms, different languages, different cross-platform frameworks – accessing that $15bn market isn’t as straightforward as it should be. Our platform is all about making it as simple as possible to build native apps and to access that market: and our white paper is an extension of that mission.

You can download and read the paper here.

And whilst I recently wrote a more technically-focused post on how developers can make sense of mobile development platforms, we want the white paper to explain the context of what is happening in the industry to a general audience, and help developers lobby the company-internal case for our platform. We hope it’s particularly helpful within companies facing the need to build for mobile but finding the landscape confusing.

We’ve filled it with facts, but it’s opinionated too: we make the case for Trigger.io as the single best solution out there.

Here’s a quick overview of the main chapters within:

The industry landscape

To start, we address the growth of mobile apps and touch on the need for businesses to build a platform-agnostic presence on mobile and on the web (closing their viral loops).

Considerations for developers

How to go about building for that need isn’t straightforward, and section two addresses some of the considerations developers face in choosing a route through the mobile programming landscape. Which platform, language and app market is worth investing in? How do cross-platform frameworks fit in?

The HTML5 opportunity

Section three unpacks and explains the strengths of HTML5 and cross-platform apps, from how they work to how to best take advantage of community tools and libraries to build the best hybrid apps possible – the best of both worlds.

HTML5 fact and fiction

HTML5 hybrid apps aren’t going to suit graphically-intense applications and games: some kinds of app are going to be better served by native builds (a false dichotomy which refuses to die). At the same time, with HTML5 standards and browser tech improving so rapidly, a lot of lazy prejudice is already outdated: here we survey some of the potential hurdles of working with HTML5, and offer some solutions for dealing with speed and native look and feel, using third-party libraries and other tips and tricks.

The paper also includes a couple of case studies of good cross-platform practise, and a high-level untechnical run-through of how to build your first Forge app, from concept to app store. We hope it encourages you to dive in and give it a go.

Here’s the link again. This will be a living document that we’ll regularly update to reflect the evolution of our platform, and the evolution of the industry. Comments? Feedback? Typos? Let us know.

New features roundup: in-app payments; video playback; toggling topbar and tabbar; icon gloss; minimum OS requirements

This week at Trigger we’re letting our teams run wild on their own projects with a ‘work on what you want’ sprint — we’ll post more about what becomes of that later on in the week.

Meanwhile, our dev team have completed their latest two-week sprint and have added some great new features to the platform. Here are the headlines:

 

In-app payments

This is our biggest update, and our most-requested in recent weeks. It’s a major feature, so we’ll post a detailed technical walkthrough on how to make sense of it soon — but details on getting started can be found in our documentation until then.

 

Reliable video playback

New API module media.videoPlay allows you to play video from a specified URL, which will display fullscreen on the device:

Define your video playback with:

media.videoPlay(url, success, error)

 

Disabling icon glossiness on iOS

You can define the icons your app uses in the config.json file (fully documented here), specifying different icons for different platforms as you wish. iOS applies a gloss effect to icons automatically, unless you command otherwise — to the great frustration of some. Now, setting prerendered to true in your icon module setting will turn this off:

{
   "modules": {
       "icons": {
           "android": {
               "16": "icon16.png",
               "32": "icon32.png",
               "48": "icon48-android.png"
           },
           "ios": {
               "57": "icon57.png",
               "72": "icon72-ios.png"
               "114": "icon114.png",
               "prerendered": true
           }
       }
   }
}

 

Show/hide tabbar and topbar programmatically

In v1.3 we added support for native tabbar and topbar components — with calls for adding and removing buttons and titles, and modifying the tint, across platforms on the fly. We’ve now added commands for showing and hiding the top and tabbar altogether.

On mobile platforms both the topbar and the tabbar will be shown by default. You can hide them with topbar.hide() and tabbar.hide(), and reshow with topbar.show() and tabbar.show(). Details can be found in the tabbar module and topbar module pages of the docs.

 

Specifying minimum required iOS/Android versions

You can now specify the minimum versions of iOS and Android you’ll allow your app to run on. Configure these requirements in your config.json file:

{
   "requirements": {
       "android": {
           "minimum_version": "6"
       },
       "ios": {
           "minimum_version": "4.3"
       }
   }
}

Making sense of Mobile Development Platforms

Mobile development platforms are so hot right now that even video ad networks want to launch one. “App cloud” is to 2012 what “pet food” was to 1998.

Why?

Firstly, mobile app development is exploding – Gartner said app store revenue would reach $15Bn in 2011 – because consumer attention on mobile devices is shifting to apps, away from the web.

But mobile app development is hard because you need to know all sorts of different languages: Objective-C for iPhone / iPad, Java for Android. And any serious app needs a remote data-store so you get into the business of provisioning servers and write Ruby, PHP or Python.

So mobile apps are a big deal, but technically complex. That’s why development platforms are springing up to help developers build them. But how can we make sense of them all? Here’s one attempt:

Mobile Development Platforms, made unnecessarily complex

Kudos to Kinvey and RWW for making this attempt and this graphic does in fact contain all the top mobile platform companies. But unfortunately, this doesn’t really help a developer trying to decide what they need, much like London Underground maps makes little sense to foreigners.

The Simple VersionTM

So let’s really boil this down so that even someone as smart as Paul Carr can understand what’s going on.

Basically, to create a mobile app, you need some code that runs on the server-side (the cloud) and some on the device.

Ok, let’s talk about the cloud bubble.

The back-end is all about aaS

The simplest server-side (aka back-end) is where you don’t actually have to write any code – instead you call some remote API that provides the service using code someone else has written. This is a BaaS – a backend-as-a-service – and examples are Parse, StackMob or MongoHQ for data-store, Pusher for notifications.

They are the top level in the server-side stack and provide the maximum ease for developers. They often provide an iOS or Android SDK so you don’t even need to deal with the mechanics of calling their server-side APIs directly with an http request.

But if they don’t provide the exact features you need so you want more flexibility, then you have to go lower in the stack and write some code to handle your server-side logic.

At this point you decide whether you want to customize a Rails or Django setup, or if you just want to write Ruby and Python and be done with it.

In the former case, you’ll want to provision a server on Amazon’s EC2 (Infrastructure-as-a-Service) and start installing software. In the latter case, you don’t need to. You can just use a Platform-as-a-Service, like Heroku or Google App Engine, that provides a Rails or Django runtime out of the box.

The client-side is native, or HTML5, or something in between

The code you write on the client-side is going to be deployed to a device that is going to be running an Operating System such as iOS or Android.

To create your app you have 4 different approaches to choose from:

1) Pure native

(top-left box)

This is where you write Objective-C on iOS and Java on Android. The upside is that you can use the full-power of the device and have the maximum flexibility in how your app works. The downside is that you need to create and maintain multiple codebases if you want to support multiple platforms. And, if you’re a web developer, you need to learn new, strange languages that need you to get yourself setup to compile.

2) HTML5

(top-right box)

Instead of writing a mobile-specific app, you could just create a website using HTML, CSS and JavaScript and just make sure it looks good in mobile browsers.

There advantages of this approach are that you can use standard web dev technologies and only need create / maintain a single codebase. Also you can use a whole bunch of JavaScript and CSS libraries such as jQuery Mobile, Sencha Touch, HTML5 Boilerplate etc. Awesome!

But there are serious downsides:

  • You can’t get distribution in the App Store and Android Marketplace
  • You can’t use powerful device features such as the camera and notification center
  • Your user-interface will probably look website-like and not app-like
  • Users will have to remember to navigate to your website in their mobile browser (a pain to type in the url) rather than just having a nice app icon on their homepage

Those disadvantages are show-stoppers because consumer attention on mobile is moving to apps away from websites.

3) Hybrid

(top-middle box)

Hybrid apps use a combination of native and HTML5 to reduce the trade-offs between them. Typically a hybrid app consists of HTML5 code running inside a provided a native Webview component (like a mini web browser) with all the advantages of (2). But there are two native elements also:

Firstly, there is some kind of ‘native wrapper’ which lets you distribute your HTML5 code as if it were a native app, so you can get distribution in the App Store and Android Marketplace.

Secondly, there is a ‘native bridge’ which allows you to access native features such the camera and notification center, but still writing your code in JavaScript rather than native languages.

Some hybrid development frameworks even let you write your own native code if you really want to, sitting side-by-side with your HTML5 code so you can mix and match as you like. Examples of development platforms for hybrid apps are PhoneGap, Appcelerator and Sencha Touch 2.

Great right? However, there are still downsides:

Complex development process:

To get the best of both HTML5 and native, some frameworks need to sacrifice the simplicity of the development process.

For example, with Appcelerator, you need to use their own IDE. And to use PhoneGap’s open-source native wrappers, you need to set yourself up for local compiles in Eclipse or XCode.

If you use a cloud build service, such as PhoneGap Build, you save yourself from needing a particular IDE or setting up for local compiles. But instead you need to zip up your code and upload it into a web form and wait several minutes while your native apps are queued and built, then download them from the web ready to test.

Not a great build / test cycle if you’re used to updating your HTML and JavaScript, hitting refresh and immediately seeing the results.

Bad UI:

Users can tell the difference between an HTML5 app and a native app, and the latter are inevitably better.

Frameworks like Appcelerator accept this reality and give you a true native UI even if the development process sucks. With Sencha and PhoneGap you’re left to make your HTML5 look like native, which can come close with a lot of effort, but can never quite be as good

That’s why you should go with Trigger.io

4) Awesome Hybrid by Trigger.io

(big box in the middle)

These problems with existing client-side hybrid frameworks are why we founded Trigger.io. We provide both a super simple development process and genuine native UI components. We’re the only mobile platform that provides this, and judging by our growth and the quotes we’ve published on our homepage, developers like it.

The reason we can do that is we run a cloud build service so you don’t have to setup your machine for local compiles but we can still add genuine native features. And we’ve been smart about building it so incremental builds take < 2 seconds, making for a very nice development flow.

Check out our screencast, or judge it for yourself by signing up for free now.

Summary

We hope this has helped you make sense of mobile platforms being able to categorize them as making the server-side or client-side part easier, and then breaking them down further within those categories.

Questions? Thoughts? We’d love to hear from you at support@trigger.io and hope this inspired you to try our hybrid app development.

How to build a location-based hybrid mobile app with reverse geocoding

Over the weekend I released a new app on the App Store and Google Play and I wanted to share how I handled the geolocation features and GMaps integration.

The app is location-based wine rating. You can track what wine you’ve consumed at restaurants by taking photos of labels and rating them. I hate going back to a restaurant and not knowing what we ordered last time – so I built this app! Check it out:

To build the app I used HTML, CSS, JavaScript and the Trigger.io platform to add native features and package it for the app stores, with these libraries:

I’ll mainly focus on the Google Maps integration part and assume existing knowledge of Backbone. But I’ll also try to highlight some other interesting snippets. We will cover how to:

  • render the map at app startup for a fast user experience when the user clicks to view
  • load Google Maps asynchronously and place markers
  • get the current location and do a reverse geocoding lookup
  • control navigation between backbone views from the native tabbar

There is a lot going on in the app so we’ll only look at snippets. For the full picture, you can see the code on GitHub.

Using the Forge build / test cycle

Throughout the development of this app, I used Trigger.io’s Forge platform to run fast build / test cycles in the device emulators so I could develop the flow with the native device features and the Forge API already incorporated. Each time I change the code it was just a couple of seconds and two commands to see the changes:

forge build

forge run ios

If you want to try building the code as native apps yourself as you follow along this post, just signup for free and download the lightweight command-line tools to get started.

I also used Catalyst – a WebKit like debugger for mobile webviews to see the logging output and debug the presentation and logic.

Basic Structure

The first step was to include all my JavaScript and HTML templates in my index.html here. Then I described the paths through the app in router.js:

// Router
wine.types.Router = Backbone.Router.extend({
  routes: {
    "rateTab": "rateTab",
    "listTab": "listTab",
    "mapTab": "mapTab",
    "mapTab/:idx": "mapTab",
    "picture": "picture",
    "rate": "rate",
    "detail/:idx": "detail"
  },
  rateTab: function() {
    state.get('rateButton').setActive();
    forge.topbar.setTitle("Rate Wine");
    if (!state.get('currentPhoto')) {
      wine.router.navigate('picture', { trigger: true });
    } else {
      wine.router.navigate('rate', { trigger: true });
    }
  },
  listTab: function() {
    state.get('listButton').setActive();
    forge.topbar.setTitle("Wine List");
    state.get('list').show();
  },
  mapTab: function(idx) {
    state.get('mapButton').setActive();
    forge.topbar.setTitle("Wine Map");
    state.get('map').show(idx);
  },
  picture: function () {
    state.set('currentPhoto', null);
    var page = new wine.views.Picture();
    page.render().show();
  },
  rate: function() {
    var page = new wine.views.Rate();
    page.render().show();
  },
  detail: function(idx) {
    forge.logging.log('... Showing detail for index: '+idx);
    var page = new wine.views.Detail();
    page.render(idx).show();
  }
});
wine.router = new wine.types.Router();

You can see that the mapTab is one of the routes and we can optionally pass it an index of a particular item to plot (when we don’t, all of the items will be plotted). Lets focus in on that map view.

Pre-loading Google Maps view

When we navigate to the mapTab in the router, we actually show a pre-existing map rather than initializing it there:

mapTab: function(idx) {
  state.get('mapButton').setActive();
  forge.topbar.setTitle("Wine Map");
  state.get('map').show(idx);
}

This is for a smoother experience since when the user clicks the map tab we’ve actually preloaded the map in the initialization code in wine.js:

state.set('map', new wine.views.Map());
state.get('map').render();
forge.logging.log('Pre-rendered map');

In views.js, the render function of the wine.views.Map prototype looks like this:

render: function() {
  var el = this.el;
  var script = document.createElement("script");
  script.type = "text/javascript";
  script.src = "http://maps.googleapis.com/maps/api/js?key=<YOUR_API_KEY>&sensor=true&callback=wine.util.initMap";
  document.body.appendChild(script);
  $('#map_container').append(el);
  return this;
}

You can signup for a Google Maps API key here. In the render function, we asynchronously load the map by embedding the script tag for it. This can then be loaded in the background and initialized when ready. The div with id ‘map_container’ is set to have width and height 100%, but is hidden until the show() function is called from the router. The callback function wine.util.initMap simply references the initMap function on the map view object.

Placing markers and controlling navigation

Here’s how we initialize the map with markers once the GMaps script has loaded:

initMap: function() {
  forge.logging.log('... Initializing map');
  forge.geolocation.getCurrentPosition(function(position) {
    $(this.el).empty();
    state.set('currentCoords', position.coords);
    forge.logging.log('Set current position:');
    forge.logging.log(position.coords);
    var latLng = new google.maps.LatLng(position.coords.latitude, position.coords.longitude, true);
    var myOptions = {
      zoom: 15,
      center: latLng,
      mapTypeId: google.maps.MapTypeId.ROADMAP
    }
    forge.logging.log('... Create map');
    state.get('map').gmap = new google.maps.Map(document.getElementById('map'), myOptions);
    forge.tools.getURL('img/blue-pin.png', function(src) {
      forge.logging.log('... Add position marker');
      state.set('currentMarker', new google.maps.Marker({
        position: latLng,
        title: "Current Position",
        icon: src,
        map: state.get('map').gmap,
        zIndex: -1
      }));
    });
    wine.photos.each(state.get('map').add);
    forge.logging.log('Created map ...');
  });
}

This snippet shows how we center the map on, and set a custom marker icon for, the current position. We’ll use the default icon to plot the other locations (where we’ve previously taken photos of wine labels). Here’s what happens when we call :

wine.photos.each(state.get('map').add);
// ...
add: function(item) {
  var latLng = new google.maps.LatLng(item.get('position').latitude, item.get('position').longitude, true);
  var marker = new google.maps.Marker({
    position: latLng,
    map: state.get('map').gmap,
    zIndex: 1
  });
  var idx = wine.photos.indexOf(item)
  google.maps.event.addListener(marker, 'click', function() {
    wine.router.navigate('detail/'+idx, { trigger: true });
    $('#map_container').hide();
  });
}

Once the map is in place, you can see how simple it is to place markers for all the objects you wish to plot and then to control navigation when those markers are clicked using the call to :

google.maps.event.addListener

Showing the map with all items or a specific item

It only remains for us to display the map when the user navigates there, either to show the locations of all the items relative to the current position, or to focus on one item in particular. To do that, here is the show function:

show: function(idx) {
  $('#map_container').show();
  wine.util.resetCurrentView(this);
  if (state.get('map').gmap) {
    google.maps.event.trigger(state.get('map').gmap, 'resize');
    var currentLatLng = new google.maps.LatLng(state.get('currentCoords').latitude, state.get('currentCoords').longitude, true);
    if (idx) {
      var item = wine.photos.at(idx);
      var latLng = new google.maps.LatLng(item.get('position').latitude, item.get('position').longitude, true);
      state.get('map').gmap.setCenter(latLng);
    } else {
      state.get('map').gmap.setCenter(currentLatLng);
    }
    state.get('currentMarker').setPosition(currentLatLng);
  } else {
    this.initMap();
    $('#map').html('<div class="title">Loading...</div>');
  }
  if (idx) {
    forge.topbar.addButton({
      text: 'Back',
      position: 'left',
      type: 'back'
    }, function() {
      $('#map_container').hide();
    });
  }
}

The idx parameter is optionally passed in – this refers to the index of the item in our model that we want to focus on. The code branches depending on whether it is passed in. If it is not, the map centers on the current location, otherwise it centers on the location of that item.

There’s no guarantee that the map has loaded already when the view is shown since the Google Maps script is loaded asynchronously. This is why we also need to check for the maps existence and display ‘Loading…’ text as a stop-gap.

Getting the current location and reverse geocoding lookup

That’s pretty much it for the map view, but there’s a couple more pieces to making the app work well with location.

In the map view functions, you’ll have seen references to the current location and it’s worth highlighting how we get that. In the initMap function, we used this call:

forge.geolocation.getCurrentPosition

We could use HTML5 geolocation instead, but the problem with that is it throws up an ugly warning to the user with the full path to the index.html file. Using the forge.geolocation.getCurrentPosition API we access true native geolocation and the warning message is much friendler:

 

The final piece is reverse geocoding: extracting an address from location data, because we want to show a list of the locations where we’ve taken photos of wine labels as well just plotting them on a map. To do that is simple with the v3 Google Maps API:

getLocation: function(coords, timestamp) {
  forge.request.ajax({
    url: "http://maps.googleapis.com/maps/api/geocode/json?latlng="+coords.latitude+","+coords.longitude+"&sensor=true",
    dataType: "json",
    success: function(response) {
      try {
        var photo = wine.photos.filter(function(item) {
          return item.get('timestamp') == timestamp;
        })[0];
        if (photo) {
          photo.set('location', response.results[0].formatted_address);
          $('#_'+timestamp+' .title').html(photo.get('location'));
        } else {
          forge.logging.log('No photo with timestamp: '+timestamp);
        }
      } catch(e) {
        forge.logging.log('--- Exception getting location --- ');
        forge.logging.log(e);
        forge.logging.log('--- Photo:');
        forge.logging.log(photo);
        forge.logging.log('--- Response:');
        forge.logging.log(response);
      }
    },
    error: function(response) {
      forge.logging.log('--- Error getting location, response:');
      forge.logging.log(response);
    }
  });
}

Controlling navigation from the native tabbar

That’s it for the location part of the app, but if you look at the code, you can see a lot more going on, such as use of Mustache templates, the native camera using forge.file.getImage, and a star rating input element (thanks to the Yahoo! User Interface Blog for the snippets).

But the remaining aspect I’d like to highlight is just how easy it is to configure a native tabbar to handle navigation in conjunction with backbone. Check-out this snippet from main.js:

forge.tabbar.addButton({
  text: "Rate Wine",
  icon: "img/star.png",
  index: 0
}, function (button) {
  state.set('rateButton', button);
  button.onPressed.addListener(function () {
    wine.router.navigate('rateTab', { trigger: true });
  });
});

forge.tabbar.addButton({
  text: "Wine List",
  icon: "img/bottle.png",
  index: 1
}, function (button) {
  state.set('listButton', button);
  button.onPressed.addListener(function () {
    wine.router.navigate('listTab', { trigger: true });
  });
});

forge.tabbar.addButton({
  text: "Wine Map",
  icon: "img/map.png",
  index: 2
}, function (button) {
  state.set('mapButton', button);
  button.onPressed.addListener(function () {
    wine.router.navigate('mapTab', { trigger: true });
  });
});

Conclusion

Using the Forge API and build/test cycle, backbone and other great libraries you can develop powerful, native mobile applications with relatively few lines of code. You can use native geolocation and the Google Maps API to add location-based features fast.

We hope this has whetted your appetite to try developing hybrid mobile apps incorporating Google Maps.

Questions, comments? Let us know at support@trigger.io. We’d love to hear from you and help with your app plans. Sign up now to get started.

New features: video capture, network status APIs available, WebSQL support, improved modal view

Our development team organizes themselves in two week sprints. We’ve just completed one and added some major new features to the platform. This sprint we added:

  • Improved Modal View with styling options
  • Video Capture API
  • Network Status API
  • WebSQL Support

Improved Modal View

forge.tabs.openWithOptions now allows you to customize the appearance of modal views on mobile.

forge.tabs.openWithOptions({
	url: 'https://trigger.io',
	title: 'TRIGGER.IO Homepage',
	tint: [41, 41, 41, 255],
	buttonText: 'Exit',
	buttonTint: [218, 174, 56,255]
});

Video Capture API

New API forge.file.getVideo allows you to develop apps that include video capture where the device supports that.

file.getVideo([params], successerror)

Arguments:

  • params (object) — object optional parameters.
  • success (function(file)) — callback to be invoked when no errors occur (argument is the returned file)
  • error (function(content)) — called with details of any error which may occur

Network Status API

New API methods forge.is.connection.connected and forge.is.connection.wifi let you change your apps’ behavior according to the connection status.

is.connection.connected()

Return boolean:

  Returns true if a mobile device has an active internet connection.
is.connection.wifi()

Return boolean:

  Returns true if a mobile device is connected via wifi.

WebSQL support

The normal web database API is now available: http://www.w3.org/TR/webdatabase/

Note: not all browsers support Web SQL http://caniuse.com/#feat=sql-storage

For example, one of the tests we run is similar to this:

var db = openDatabase('mydb', '1.0', 'example database', 2 * 1024 * 1024);
db.transaction(function (tx) {
    tx.executeSql('CREATE TABLE IF NOT EXISTS foo (id unique, text)');
    tx.executeSql('INSERT INTO foo (id, text) VALUES (1, "foobar")');
});

db.transaction(function (tx) {
    tx.executeSql('DROP TABLE foo');

    // known to fail - so should rollback the DROP statement
    tx.executeSql('INSERT INTO foo (id, text) VALUES (1, "foobar")');
    forge.logging.error("INSERT into non-existent table succeeded!");
}, function (err) {
    forge.logging.info("error callback invoked, as expected");
});

db.transaction(function (tx) {
    tx.executeSql('SELECT * FROM foo', [], function (tx, results) {
        forge.logging.info("row: "+results);
    });
});

Using Geolocation and GMaps with Trigger.io, Appnovation gets first-time App Store approval

We’ve been supporting enterprise development shop Appnovation in building their first Forge app. The app – for NeedAnAccountant.org – lets (Canadian) users find their nearest accountants, using our framework to wrap their app to native (packaged for iPhone and Android), handle cross-domain requests and native geolocation features through our API.

Google Maps and Geolocation API integration

Under the hood, the map is integrated with simple HTML and JavaScript with the standard Google Maps API, with calls to Trigger’s Forge handling geolocation:

google.maps.Map.prototype.setCurrentLocation = function(fn) {
	forge.geolocation.getCurrentPosition(function(position) {
		map.currentLocation = {
			"lat": position.coords.latitude,
			"lng": position.coords.longitude
		}
		fn();
	}, function() {
		// Some dummy data if geolocation failed
		map.currentLocation = {
			"lat":undefined,
			"lng":undefined
		}
	});
	fn();
}

Though it can be done with HTML5, geolocation is one feature that’s handled better through a bridge to native – using forge.geolocation. For one thing, this way we don’t push ugly pop-up confirmations to the user on every run.

Forge also handled cross-domain requests within the app – giving us a lot more freedom than a straightforward HTML5 mobile-app:

forge.request.ajax({
	url: requestUrl,
	dataType: 'json',
	timeout: 5000,
	success: function(data, status) {
		forge.logging.info('[search-nearme] found '+status.length+' results near user');
		$("#loader").hide();
		displaySearchResultsMap(data);
	},
	error: function(status, errorThrown){
		forge.logging.error('[search-nearme] '+status);
		$("#loader").hide();
		$("#error").show();
	}
});

First-time App Store approval

Best of all, the app was accepted into the iOS Store on its first submission – we’re proud to see our framework is strict, neat and tidy enough to pass through Apple’s approval process without any setbacks.

Many App Store rejections happen due to use of undocumented APIs or failing to include interface elements such as launch images. We made sure to implement our native wrapper to follow Apple’s documentation as closely as possible, so our customers don’t need to worry about it. Appnovation were able to prepare their app, and deploy it quickly, on deadline, with no hitches – keeping their client happy.

Canadians especially should check out the NeedAnAccountant.org app on iOS and Android stores, and find out more about Appnovation’s work at Appnovation.com.

Next steps for Appnovation

When we first reached out to Appnovation, we realized that they are always interested in providing innovative solutions to their clients and we had one we figured they’d love to test out.

“Anyone who has tried to deploy an HTML5 app in native iOS/Android wrappers will tell you how much of their time went into installing, setting up and correctly configuring each native project and IDE, the cumbersome build & test cycles,” they told us. “Add code-signing certificates, and an unfamiliar XCode interface to the mix, and curve gets steeper for HTML5 developers.”

So we’re psyched to hear they’re interested in our approach and will be making sure to look at Trigger for future projects if there is a technology fit. They have a killer roster of enormous, Fortune 500 clients that demand the highest-quality apps for their businesses. We’re proud to see our framework stacking up to such high standards.

Appnovation told us:

“The great thing about Trigger is we never had to touch XCode or Eclipse. We learned a few simple shell commands: forge create, forge build, forge run, forge package. Trigger centralizes all configs in a single, intuitive JSON file, and we were off creating & testing native builds in minutes.”

Appnovation have been an awesome customer, and we thank them for all their kind words about using our framework.

Thanks, guys. Here’s to the next one.

Screencast: Trigger.io Catalyst in action

Last week we shared our first screencast of Forge in action, walking you through the build-test workflow – this week, we wanted to show you how you can use Catalyst to easily test and debug your app. Check it out:

Normally, debugging for mobile – without the same tools available as you get for web development – is a real pain. Catalyst makes it simpler, giving you a full WebKit-like debugger for mobile.

Catalyst is our hosted version of the Weinre project, and features a panel for debugging your Forge API calls, alongside areas to inspect your code and JavaScript log. You can use it with the regular mobile browser to debug mobile web pages as well as with Forge to debug HTML5 code that has been wrapped inside a native WebView.

In the video, I show how to debug the app we built in the last screencast, and test it live in the iPhone simulator. You can use Catalyst to:

  • Navigate and alter the DOM, including styling with the Elements tab
  • Query JavaScript objects and test code inside the WebView with the Console tab
  • Observe network usage and execution order
  • Log debug messages to the console and track Forge API calls when building hybrid apps  with Trigger.io Forge

Try it out and let us know how you get on – firing all your support questions to support@trigger.io, where we’ll gladly help you out.

We had a great response from last week’s video, and we’ll hope to do more of these down the line and drill a bit deeper into Forge – let us know what you’d like us to cover, and make sure you follow us on Twitter to catch the next installment!

April round-up: Doubled usage (again!), Trigger Toolkit and v1.3 released

v1.3 Platform Upgrade

We’ve launched the 1.3 version of our platform including a new visual toolkit to make it even simpler to use our cloud build service.

Amongst the many changes and improvements over the past month that are rolled into this release, we added:

To upgrade, the format of your config.json needs to be updated, but we’ve made that easy – just run:

forge migrate

Trigger Toolkit

Today we’re also releasing a new way to build and manage Forge apps, called the Trigger Toolkit.

This makes it even easier to use our cloud build service to create apps for iOS, Android and the web:

  • Visual interface to create, build and run apps
  • Console to view status logged messages
  • Simple installer for OSX and Windows
  • Built-in documentation to speed up your development
  • Ability to get in touch with our support team easily from within our tools

Oh, and the command-line tools come bundled with it, so you can still use them for all your automation needs.

We’d love your feedback, you can download it now from our website.

Questions? Let us know on our StackOverflow tag and we’ll respond straightaway.

Doubled again – 20,000 actions in April!

Finally, thanks so much to you for your continued support. We doubled usage again in April with more than 20,000 build, test and package actions:

May is shaping up to be a fantastic month as well with many new features and fixes planned. Stay tuned on Hacker News and Twitter.

How to build hybrid mobile apps combining native UI components with HTML5

Last week we showed a screencast of Trigger.io Forge in action, this week we’ll explain how you can use a native top bar and native tab bar along with your HTML / CSS and JavaScript in a hybrid app created with Trigger.io.

We’ll add more native UI components in the future, but here’s how you can get started creating a beautiful, responsive mobile apps for iOS and Android using just web technologies.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

These are screenshots of a simple recipe app that we created using the Trigger.io Forge native UI components, and we’ll explain how we built it. Along the way we’ll show you how to:

  • Configure Trigger.io Forge to add native top bar and tab bar elements to your app
  • Style the native elements
  • Add listeners to and control the native elements from JavaScript

Please feel free to base your own projects on this – it’s a great springboard for a new project! The code is hosted on github here: https://github.com/trigger-corp/forge-template-list-and-detail

Create your app and add a top bar

You’ll need to use the Trigger.io Forge framework to be able to get native UI components using only web technologies. Get started by signing-up. There’s complete documentation on how to get setup and once you’ve done that just run:

forge create

You’ll be prompted for the app name and once the command completes, you’ll be setup with the code for a ‘Hello World’ app in the src subdirectory. Let’s start by adding a native top bar and testing that in the Android emulator.

Replace your existing src/config.json with this:

{
    "author": "amir@trigger.io",
    "config_version": "2",
    "description": "View ingredients for your favorite recipes",
    "modules": {
        "is": true,
        "logging": {
            "level": "INFO"
        },
        "prefs": true,
        "request": {
            "permissions": []
        },
        "tools": true,
        "topbar": true
    },
    "name": "Recipe list",
    "platform_version": "v1.3",
    "version": "0.1"
}

Enabling the topbar is as simple as setting: “topbar”: true in the modules configuration. Let’s also clean-up index.html until we’re ready to customize it:

<!DOCTYPE html>
<html>
	<head>
	</head>
	<body>
		<div class="content">
			Hello world!
		</div>
	</body>
</html>

You can then run and test the app so far using the following forge commands:

forge build

forge run android --android.sdk ~/Desktop/android-sdk-macosx

Any problems at this point? Check out our FAQ, ask the community on StackOverflow or contact support@trigger.io.

Configuring a tab bar

Great! It turns out that adding the tab bar at the bottom is just as easy – you simply enable the module by adding “tabbar”: true to the src/config.json:

{
    "author": "amir@trigger.io",
    "config_version": "2",
    "description": "View ingredients for your favorite recipes",
    "modules": {
        "is": true,
        "logging": {
            "level": "INFO"
        },
        "prefs": true,
        "request": {
            "permissions": []
        },
        "tools": true,
        "topbar": true,
        "tabbar": true
        },
    "name": "Recipe list",
    "platform_version": "v1.3",
    "version": "0.1"
}

But before that will look good and behave well we need to add some buttons and listeners so we can execute JavaScript to handle the page transitions when you click on each tab. To do that, let’s add a JavaScript file called src/js/main.js:

// A helper function so that when we change tab the web view scrolls to the top of the new page
var scrollTop = function () {
	setTimeout(function () {
		document.body.scrollTop = 0;
	}, 0);
}

// This is the method we are going to call when a tab button is pressed
var updateRecipes = function (search) {
	scrollTop();
	// For now just pop up a message with the tab which was pressed
	alert(search);
}

// Set a better title for the topbar
forge.topbar.setTitle("Recipe shopping list");

// Add our 3 tab buttons to the tabbar and add press listeners
var starterButton = forge.tabbar.addButton({
	text: "Starters",
	icon: "img/tomato.png",
	index: 0
}, function (button) {
	button.onPressed.addListener(function () {
		updateRecipes("starter");
	});
	// This is the default button, activate it immediately
	button.setActive();
	updateRecipes("starter");
});

var mainButton = forge.tabbar.addButton({
	text: "Mains",
	icon: "img/pizza.png",
	index: 1
}, function (button) {
	button.onPressed.addListener(function () {
		updateRecipes("main");
	});
});
var dessertButton = forge.tabbar.addButton({
	text: "Desserts",
	icon: "img/strawberry.png",
	index: 2
}, function (button) {
	button.onPressed.addListener(function () {
		updateRecipes("dessert");
	});
});

Here we’ve using the forge.topbbar.setTitle API call to alter the title at the top, and then used forge.tabbar.addButton to configure the tab bar buttons including adding a listener to execute JavaScript when they are pressed. We reference this file from src/index.html:

<!DOCTYPE html>
<html>
	<head>
		<script src="js/main.js"></script>
	</head>
	<body>
		<div class="content">
			Hello world!
		</div>
	</body>
</html>

You can find the images referenced in src/js/main.js in the example code in Github. You’ll also need to copy those across to your src/img directory.

Now if we build and test (this time on iOS), we see this:

Creating list views and controlling navigation

Ok, now we have the native UI elements in place, we need to create our list view and connect up the tabs so they trigger transitions.

We’ll use the lightweight zepto.js library to help us handle the DOM manipulation. We’ve blogged before on how to create fast HTML5 mobile apps using zepto.js and backbone.js. We’ll create the recipe list using HTML / CSS / JavaScript in the main part of the view since it’s simple to create good-looking lists this way.

First, let’s embed the recipe data and the zepto.js library. You can do that by downloading data.js and zepto.js from the example in Github and putting them in your src/js directory.

Then we update the updateRecipe function in src/js/main.js – this is called when a tab bar button is pressed:

var updateRecipes = function (search) {
	scrollTop();
	$('.content').html('<ul class="list"></ul>');
	$.each(data[search], function (i, recipe) {
		var el = $('<li><img src="'+recipe.thumb+'">'+recipe.title+'</li>');
		el.on('click', function () {
			scrollTop();
			$('.content').html('<div class="recipe"><h3>'+recipe.title+'</h3><img src="'+recipe.img+'"><ul class="ingredients"></ul><p><a href="#" onclick="forge.tabs.open(\''+recipe.source+'\')">View full recipe</a></p></div>');
			$.each(recipe.ingredients, function (i, ingredient) {
				$('.ingredients').append('<li>'+ingredient+'</li>');
			});
			forge.tabbar.setInactive();
		});
		$('.list').append(el);
	});
}

Now to complete the app, all we need to do is add some simple styling to the list and reference the JavaScript files in src/index.html:

<!DOCTYPE html>
<html>
	<head>
		<style>
			body, html, li, ul {
				padding: 0;
				margin: 0;
			}
			body {
				font-size: 1.2em;
			}
			.recipe {
				text-align: center;
			}
			.recipe img {
				max-width: 80%;
			}
			.recipe li {
				display: block;
				font-size: 0.9em;
				padding: 2px;
			}
			.list {
				margin: 0;
				padding: 0;
			}
			.list li {
				display: block;
				border-bottom: 1px solid #aaa;
				padding: 0;
				margin: 0;
				width: 100%;
				overflow: hidden;
				white-space: nowrap;
				text-overflow: ellipsis;
			}
			.list li img {
				height: 50px;
				width: 50px;
				padding: 2px 7px 2px 2px;
				vertical-align: middle
			}
		</style>
		<script src="js/zepto.js"></script>
		<script src="js/data.js"></script>
		<script src="js/main.js"></script>
	</head>
	<body>
		<div class="content">
		</div>
	</body>
</html>

That’s it

You should now be able to build and test the app to look like the screenshots at the top of this post. As an optional extra, you can also explore different styling for the topbar and tabbar by trying out the following API calls:

So now you can create rich hybrid apps, using real native UI components using Trigger.io! Let us know your feedback and how you get on.

Any problems at any point? Check out our FAQask the community on StackOverflow or contact support@trigger.io.