Meteor is an amazing tool to create applications for the mobile devices. This guide shows you how to use Meteor to build, test, and send an app to the app stores.

Note This tutorial is a bit long and goes through all required steps from initial app creation to download from the app stores! You’ll need to walk through several steps that are listed in this agenda:

The app: Reactive H-Ball

In this tutorial you are going to build a simple application that helps you answer life’s most pressing questions, such as “Will I be pretty?” or “Will I be rich?” My last name starts with an H and the image we’ll use is that of a ball, so it’s probably a good idea to give it a name to resemble that. Because it uses Meteor’s reactive powers it shall be called the Reactive H-Ball. Not to be mistaken for a magic 8-ball, of course.

Building the application

We implement limited functionality on purpose to focus on the specifics of mobile applications. All the app should do is to display an answer to a yes/no-question when the device is shaken. We’ll use an animation to make the otherwise quite boring application more mysterious.

Find the application sources on GitHub:

The basics

Let’s start with a shell command – assuming you have Meteor installed:

$ meteor create reactiveHBall

Creating a new application in the Terminal

This leaves use with the new application and the three file structure:

  • reactiveHBall.css
  • reactiveHBall.html
  • reactiveHBall.js

Because the application will be so simple, we may keep this structure. All we need is to add a folder named public/, which is where the graphics file shall live.

Next, let’s take care of the core functionality. We’ll use a single template named hball, which will be used to display all content. It needs a single template helper {{answer}} and a button. Why a button? It’s easier for us to test this for now and only later substitute the type of event we’re listening to, so the button fullfils essentially the same purpose as a shake in the final app.

<template name="hball">
  <button id="shaker">Shake</button>
  <div id="ball">
    <div id="text">

The answer is stored inside a Session variable called answer. As such, the template helper looks like this:

  answer: function () {
    return Session.get('answer');

All answers are stored inside a simple array named answers. The value for the Session variable is set randomly by the click event:{
  'click button': function () {
    Session.set('answer', answers[
      // get a random number from the answers array
      Math.floor(Math.random() * answers.length)

Adding more style

Now that the core functionality is in place, let’s add some style.

First, we need the H-Ball to be an actual ball. We are dealing with various screen sizes, so we must ensure that the ball scales well to various resolutions. Therefore we need a responsive approach, however we keep it simple (put this in your CSS file):

#ball {
  position: relative;
  display: inline-block;
  width: 100%;
  height: 0;
  padding: 50% 0;
  border-radius: 50%;
  background: #8fa4cd;
  color: white;
  font-size: 28px;
  line-height: 0;
  text-align: center;

Much better already. Now for the animation. This will be tied to the event (the button click for now, later on the shake):{
  'click button': function () {
    // fade in the text to make it more mysterious
    $('#text').fadeOut('1200', function () {
      Session.set('answer', answers[
        Math.floor(Math.random() * answers.length)

Simple enough, right? Let’s check out how it looks in an actual mobile device. There is an easy way to do so if you are using Chrome. Open up the developer tools and click on the icon next to the magnifying glass that looks like a smartphone. That brings up the device emulation that allows you to check how the screen looks on various devices.

Device Emulation in Chrome

Add mobile platforms

Back to the shell, we need to issue some commands to add support for iOS and Android. For adding iOS support you must have a Mac OS X machine and downloaded and opened Xcode at least once (opening it will make you accept some licenses, plus it guides you through creating the required Signing Identities and Provisioning Profiles – you need this for publishing to devices and it requires an Apple Developer account).

Issue the following commands for iOS support:

$ meteor install-sdk ios
$ meteor add-platform ios

Issue the following commands for Android support:

$ meteor install-sdk android
$ meteor add-platform android

Now the application can be run on both Apple devices as well as Android phones and tablets.

Running on device emulators

Let’s run on a device emulator first.

Run the app on the Android simulator:

$ meteor run android

The app on the Android simulator

Can you see it? We have too much text in the upper half of the screen. We should fix that. Maybe it helps to remove the shake button, but first, how does the app look on iOS? Run the app on the iPhone simulator!

$ meteor run ios

Preview on the iPhone simulator

That looks nice enough. Now shake it, baby!

Adding the shake

There are many ways to add device-specific functionality to a Meteor application. The simplest is to add a Meteor package (Isopacks). That way you do not have to deal with Cordova plugins (although you could) and deal with the limitations regarding version management (tl;dr: Meteor is not smart enough to determine version constraints from Cordova plugins, only from proper Isopacks).

The shake package is perfect for our purposes, add it via

$ meteor add shake:shake

You can find additional information regarding this package at its GitHub page. This is the code you will need to add to the JavaScript file to enable shaking for an answer (this should only run on the client!):

// avoid accidental "shakes" with a high enough sensitivity
var shakeSensitivity = 30;

// watch for shakes while the app runs
Meteor.startup(function () {
  if (shake && typeof shake.startWatch === 'function') {
    shake.startWatch(onShake, shakeSensitivity);
  } else {
    alert('Shake not supported');

// onShake show an answer
// debounce ensures the function does not execute multiple times during shakes
onShake = _.debounce(function onShake() {
    console.log('device was shaken');
    $('#text').fadeOut('1200', function () {
      Session.set('answer', answers[
        Math.floor(Math.random() * answers.length)
  // fire the shake as soon as it occurs, 
  // but not again if less than 1200ms have passed
1200, true);

Running on hardware devices

Do you know your machine’s IP? Find it out, mine is You’ll need it because the mobile device must connect to your machine somehow.

First, we need to create a new file named mobile-config.js in the root of the project. For now it will only contain a single line:


By default, Cordova applications (this is the technology used by Meteor to create mobile applications) may not access any URLs unless they are whitelisted. Adjust the line above to your IP address and port where Meteor runs.

On Android you must first enable debugging over USB on your phone. Connect your Android phone, unblock it and start Meteor using

$ meteor run android-device --mobile-server

Of course the device must be able to access the IP address. You can check first by running meteor without any mobile options and trying to open the page using the mobile browser.

It takes a couple of seconds, now you will be able to run the application on the device. I’m testing with a Nexus 5 and here comes the big surprise: The ball is a square!

This is one of the reasons you must test not only with a simulator, but also on actual devices. Otherwise this square would have gone unnoticed, perhaps even get published to the Play Store!

Is iOS any better? Connect your iPhone/iPad and rush to the shell:

$ meteor run ios-device --mobile-server

The previous command opens XCode and does not actually run the app on the device. Once Xcode is opened you can select any of the simulated or connected devices. I select the hardware device and test. Fortunately all looks as it should and even the shakes work fine.


Now that the core application is done, we can focus on some nice-to-haves.

Fix the appearance

Let’s address the issue with the circle being a square on Nexus first – it is as simple as adding the proper viewport to the HTML head section:

  <title>Reactive H-Ball</title>
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />

Next, get rid of that shake button. I simply uncomment it, so I can easily re-enable it for debugging purposes later.

Finally, we’ll use an image from the public/ folder instead of the <h1> title:

<img src="title.png" alt="The Reactive H-Ball" width="100%" />

Now why do we use an image? In this application we do not need any images, but in most other apps you will have some resources in public/ you’d want to share, so this sample app should cover that use case.

Offline applications

This application is so small and simple, it does not even take a server to do what it’s supposed to. In fact, many mobile applications should be able to survive without a network connection.

The first thing our Reactive H-Ball app should do upon startup is disconnect itself from the server. This is because Meteor does not currently allow/support client-only applications, and as just mentioned, Reactive H-Ball just doesn’t need a server. So, yes, we’re creatively working around the current limitations of Meteor a bit, but it’s okay. If app needs to connect to a server you should skip this part.
Add this line to the startup block:


When you run on a device now, you do not have to pass the mobile-server parameter anymore, it isn’t used anyway. Note: This will disable our ability to update the application code without re-submitting the app to the Play/iTunes Store!

All application files should come from the device itself. There is no need to request them from a server and, in fact, there is no server to request them from since you’ve already disconnected. Adding the appcache package enables the application to store those application files in the localStorage. This also includes the contents of the public/ folder, which is where the title image lives:

Meteor offers a package called appcache which uses localStorage to store our apps’s assets. Since Reactive H-Ball is a mobile application it will not benefit from adding the package because it already has all asset in the app that was installed. Using the appcache package is only recommended for applications that run in the browser and require support for offline usage.

Note that, if we were utilizing any MongoDB collections in our app, we’d want to add a means of persisting our data when the app closes. Currently, GroundDB is the most suitable solution for handling that situation.

Preparing for distribution

The app is almost finished, but we have a few final steps to complete before it is ready for the app store shelves. That includes adding some meta information, images, and submitting a binary build of the app to the stores (which you need to have an account for, obviously).

App information

Remember the mobile-config.js file we used for whitelisting the URL? It’s also used to set all meta information for our app. Adjust the block as needed:{
  // the bundle ID must be unique across the entire app store
  // usually reverse domains of the creators are used
  id: 'de.yauh.reactivehball',
  version: '1.0.0',
  name: 'ReactiveHBall',
  description: 'The all-knowing H-Ball answers your questions about life',
  author: 'Stephan Hochhaus',
  email: '',
  website: ''

Icons and Splash Screens

For each device our application needs a dedicated icon and splash screen. Although there are many generators on the web that allow you to upload images and let you download the finished assets I prefer to use Ionic locally. If you haven’t used Ionic yet (it’s simple, but requires a bunch of shell commands to create a new app, add platforms, and generate resources), use a web service like Image Gorilla or check out the hybrid:assets package.

Once the images are available you can add them to the application folder and adjust mobile-config.js (this is just an extract, there are many more sizes to add):

  // iOS
  'iphone': 'mobile-resources/ios/icon/Icon-60.png',
  // Android
  'android_ldpi': 'mobile-resources/android/icon/drawable-ldpi-icon.png',

  // iOS
  'iphone': 'mobile-resources/ios/splash/Default~iphone.png',
  // Android
  'android_ldpi_portrait': 'mobile-resources/android/splash/drawable-port-ldpi-screen.png',

Build and deploy the application

Step 1 is to actually build the application. Use

$ meteor build --server= ../build

That will ensure that a) the building process works and b) the files necessary for submittal to the app store get created.

Let’s assume that at some point the H-Ball application should contact the server. In that case you may want to deploy it on the web. The simplest way is to use mup.

Take some screenshots

When putting the app on the app stores you want people to see what the app looks like. Now’s the time to take a few screenshots of your app in action on both Android and iPhone devices.

Submitting to the stores

We start with the following file structure:

  • app contains the actual application sources
  • build contains the output of the meteor build command
  • build/android holds the necessary files for submitting to the Play Store
  • build/ios holds the necessary files for submitting to the iTunes Store

File structure of our project

Google Play

You need a Google developer account (one-time fee of $25). Once you have an account you can submit your applications at the developer console. Before we can do so, you must sign your application bundle or apk file.

Step 1 is to create a key. This key is essential and you mustn’t lose it. If the key is lost, you will then lose the ability to publish updates! Create a key via the shell command:

$ keytool -genkey -alias reactive-hball -keyalg RSA -keysize 2048 -validity 10000

Back up the cert file using the command

$ keytool -exportcert -alias reactive-hball -file hball.cer

Step 2 is to sign the unaligned application file. Using the command in the previous step (when we used meteor build) created an android/ directory inside our project. There you can find a file named unaligned.apk. This needs to be signed.

Open a shell and cd to the android directory. The command used for signing the apk-file is

$ jarsigner -digestalg SHA1 unaligned.apk reactive-hball

Once the file is signed, it needs to be aligned. Do that by issuing

$ ~/.meteor/android_bundle/android-sdk/build-tools/21.0.0/zipalign 4 unaligned.apk production.apk

Now you have a new file production.apk. This should be submitted to the Play Store.

Step 3 is done in the Developer Console – a web interface for submitting your app. I hope you like filling out forms, because you need to do this quite a bit.

Editing product details on Google Play Store

Once you have uploaded the apk file you need to fill out the product details and upload some screenshots and icons. Green checkmarks will let you know when you’re done. Once all is green you can hit the Publish app button.

iTunes Store

You need an Apple Developer account, which will need to be renewed annually or else your apps will be removed from the store! That also means you need to pay $99 each year to stay listed. Got that? Then you’re ready to submit your app.

I assume you have set up a signing identity and provisioning profile (basically Xcode walks you through this, it’s very simple. If you need guidance, this link is excellent!). It’s probably equivalent to the signing and aligning for Android, just in the Xcode interface.

Start by registering an App ID. Click on the link (yes, that one and add the app information.

Registering an explicit App ID

Next, head over to the iTunes Connect site. You will need to set up your application before we need to open Xcode. Go to My Apps and click on the + sign to add a new iOS app. Most of the fields take whatever you want, except for the bundle identifier. Make sure to use the correct one (case matters!).

Create a new iOS app in iTunes Connect

If you like, you can start digging through even more forms to prepare your marketing collateral to publish the app. You might as well do it now, because you’ll have to eventually. Fill in all information and upload screenshots and come back here when you’re done.

Open the Xcode project file Reactive H-Ball.xcodeproj. Make sure an iOS device is connected via USB; you will need it.

Check the project settings under General and make sure there is a team selected for Identity and the correct bundle identifier is used. If the bundle identifier is wrong, you can adjust it in the Info settings.

Project Info settings in Xcode

One more thing – set the deployment target to be iOS 7.1. Dealing with legacy versions of iOS requires some more work and a minimum of iOS 7.1 requires less icons and splash screens, hence making life easier overall.

Deployment Target iOS 7.1

Select the hardware device just like you did when running on the device. Now open the Product menu and click Archive.

Creating an archive in Xcode

Another window pops open with a list of all available archives. Perform a validation of the archive before you submit it to the App Store.

If that went well, you should now (finally!) be able to click on Submit to App Store… You’ll be rewarded by a “Submission Successful” message.

Submission Successful

Unfortunately, in my case I saw an error message. There were some app icons for iPad missing, so I needed to fix this. Back to the project settings and clicking on Use Asset Catalog for the App Icons.

Use Asset Catalog for App Icons

A new screen will open and you can drag the missing icons (in my case icon-76.png and the retina version) into the asset library).

Drag missing icons in asset library

Alas, the app will still not go to the app store. More hoops to jump through! Return to the iTunes Connect site. Before the app can be released, it must be tested. D’oh. You need a user with the “Internal Tester” role and active Test Flight (All under the prerelease tab).

Prerelease Builds

Once you invited a user (go to internal testers and select one or more users and click Invite) all testers will receive an email that they can start testing:

Invitation to test using Test Flight

Install the TestFlight app on your device and then inside of it, open up your new mobile app.

Once everything is tested, you should add the build in the iTunes Connect Versions tab of your application under Build.

Adding a build

With a build added you can finally submit the app to be reviewed by Apple. Let the waiting begin.

1.0 Waiting for Review

Now might be a good time to check the current wait time for app reviews at

Apple will send you emails about status changes for your app. Be prepared to wait a few days.

Status changes for App Review at Apple

Get the apps

Get it on Google Play

Available on the App store