So you want to build a "web app" ...
Downsides of web apps: Performance, Functionality/Access to Hardware, Marketing: App/Play-Store
Me
@colinfrei
First a tiny bit about me: I work as a web developer at Liip, coding in PHP and JavaScript. I've been involved with the Mozilla Community for about a year, mostly because I'd heard about FirefoxOS, and I've been playing around with different parts of it ever since.
I'm definitely not an expert on everything I'm going to talk about, but I have spent too much time playing around and reading specs on a bunch of this stuff.
Topics
Web APIs
Web Activities
App Manifest
Marketplace
I want to give you a little insight into four different things: Web APIs, Web Activities, the App Manifest, and Marketplaces for distributing your app. Hopefully that covers everything you need to turn your existing javascript app or site into a full mobile app.
APIs
WebTelephony
Vibration API
WebSMS
Idle API
Screen Orientation
Power Management API
Mobile Connection API
TCP Socket API
Geolocation API
WiFi Information API
Device Storage API
Contacts API
Mouse Lock API
Open WebApps
WebBluetooth
Network Information API
Battery Status API
Alarm API
Browser API
Time/Clock API
Push Notifications API
Permissions API
WebFM API
FileHandle API
Network Stats API
WebPayment
Ambient light sensor
Proximity sensor
There's a whole bunch of APIs that have come up, this isn't a complete list either. Some are already very widespread, think Geolocation, others not at all. I've never seen WebBluetooth in the wild yet for example, but it sounds like fun. WebNFC is on the list of things to come.
All the apps on the FirefoxOS phones are written using these APIs, and so they let you do most things a phone can. You could theoretically write your own dialer using the WebTelephony app for example.
We'll look at a few fun ones to give you some ideas of how you can extend your existing app easily.
Ambient Light
window.ondevicelight = function (event) {
console.log("Ambient light: " + event.value + " lux");
};
W3C Specification
Ambient Light is a w3c working draft. It adds an event on the window object, which gets called with the lux value the ambient light sensor measured.
This is really straightforward, but allows cool things like allowing you to change the style and the contrast of your site/app based on the ambient light.
Battery Status
var battery = navigator.battery;
/*
battery.level: Battery level between 0 and 1
battery.charging: true/false
battery.chargingTime: Time left to fully charge, in seconds
battery.dischargingTime: Time until fully discharged, in seconds
*/
console.log("Battery level: " + battery.level * 100 + "%");
navigator.battery.addEventListener(
"levelchange",
function() {
// Update display with current battery level
}
);
W3C Specification
The Battery Status API is a W3C Editors Draft. It allows you to get information about the battery by using the navigator.battery object.
Vibration API
navigator.vibrate(1000);
navigator.vibrate([200, 100, 200]);
W3C Specification
This one used to be called WebVibrator , that was changed to Vibration API after all the bad jokes had been made too many times.
The Vibration API is simple as well, it allows you to call a function on the navigator object and either pass it a length it should vibrate (in milliseconds) or an array, where it'll switch between vibrating and not vibrating. So in the bottom example, it'd vibrate for 0.2 seconds, pause for 0.1 seconds and vibrate again for 0.2 seconds.
Push Notifications
// Setup Push Notifications
var reg = navigator.push.register();
reg.onsuccess = function(e) {
var endpointUrl = e.target.result;
// save endpointUrl to app server
}
navigator.mozSetMessageHandler('push', function(m) {
var version = m.version;
// do something based on version
});
//Unregister
navigator.push.unregister(endpointUrl);
SimplePush on MDN
Push notification require a bit more code, since you need a backend to it, but they're still fairly easy. You simplyregister a pushnotification, and the browser will return an endpointUrl, which is on a third-party server. You send that URL to your backend server, and whenever you want to send a push notification to the device, you can PUT to that endpointURL.
That will cause the notification to be pushed to the device, which will broadcast a message, and that message can cause other things to happen, like you notifying the user. We'll see how messages are dealt with in more detail later on.
(Local) Notifications
var notification = navigator.mozNotification;
notification.createNotification(
"Title",
"Notification Text",
iconUrl
);
Open WebApps
var appInstall = navigator.mozApps.install(manifestUrl);
appInstall.onsuccess = function(data) {
// App installed!
};
appInstall.onerror = function() {
console.log("Install failed: " + appInstall.error.name);
};
WebPayment
var payment = navigator.mozPay(JsonWebToken);
payment.onsuccess = function() {
// ...
// Profit!
}
Payments have a bunch of parts, having to do with authentication, identification, payment processing and so on. It's all specificed in a set of specifications under the name PaySwarm. I haven't played around with it much, but my understanding is that you wrap all the payment information into a so called Web Token, and the payment itself is simply a call to mozPay(), passing in that token and returning the result.
Web Activities
Next up: web activities.
Web activities are similiar to Web Intents , they allow you to build links between apps. There are two sides to that.
Two sides
Use another app's functionality
Offer functionality
One side is when you want to reuse a different apps functionality.
Examples might be sharing something, or taking a picture - you want to offer the functionality to the user of the app, but don't want to implement it all yourself, so you call a web activity.
Sharing might offer a selection of Twitter, Facebook or Google Plus, while take a picture will offer you the Camera or the Gallery.
The other side is if you want to offer functionality from your app, so that other apps can take advantage of it.
What you can do is register your app for existing activities, or you can create your own activities. When creating your own activity, it's considered best practice to prefix it with your URL/Domain, to avoid namespace collisions.
Sharing an Image
var share = new MozActivity({
name: "share",
data: {
type: "image/*",
number: 1,
blobs: [new Blob($('#imgToShare')[0], {type: "text/png"})]
}
});
share.onsuccess = function() {
// Do stuff
};
share.onerror = function () {
// handle error
};
Let's take a closer look at the first scenario first.
Basically all we have to do is instantiate a new object, and tell it what kind of activity we want to execute.
As soon as that object is instantiated the user will see the interface to choose what he wants to do.
Offering an Activity
"pick": {
"href": "./pick.html",
"disposition": "inline",
"filters": {
"type": ["image/*","image/jpeg","image/png"]
},
"returnValue": true
}
The alternative, if our app wants to offer functionality, needs two steps - we need to register the activity and handle it.
Registering the app happens in the app manifest, where we add Objects that configure it.
pick is the name of the activity in this case, and additionally we define the href, which is the page that should be opened when the activity is called. The disposition has two options, either inline or window
returnValue is a boolean, stating if the call will return a value or not.
Finally, filters specify what image types this activity should be offered for. This can be easy like in this example, but can also have things like ranges with a minimum and maximum, a pattern, and more.
Handling the Activity
navigator.mozSetMessageHandler('activity', function(request) {
var options = request.source;
if ("pick" === options.name) {
// Do something with the image
}
});
To handle the activity, what we need to do is
Things to note:
Activity pages have to abide to same origin policy as well
Support for dynamically registering activities is coming but not ready yet
Firefox OS Activities
browse
Browse a gallery to select a Photo
configure
Change phone settings
costcontrol/*
Cost control functionality
dial
Call someone
new
'add something': contact, email, sms
open
'open sthg': contact, gallery, music, video
pick
Retrieve data: contact, image, email
record
Take a picture or record video
save-bookmark
Save a bookmark to the home screen
There are a bunch of activities that FirefoxOS offers by default, which you can take advantage of or use as well.
Cost control allows you to get the available balance and data/voice usage
Firefox OS Activities (2)
share
Share via bluetooth/email, also used to set picture as wallpaper
view
Open sthg: browser, email, pdf, video
update
Update a contact
Firefox on Android also allows you to use some of the activities, and has a mapping to Android's intents.
A 'pick' activity maps to Android's GET_CONTENT Intent.
Open Web App manifest
So what makes it work, what makes the whole thing an app?
All it takes is a manifest, which is a json file containing some metadata about your app.
Basic Manifest
{
"name": "Cool App",
"description": "This app lets you do really awesome stuff"
}
This is a very basic manifest, and includes the two required fields, a name for your app, and a short text describing what your app does.
A bit more data
{
"name": "Cool App",
"description": "This app lets you do really awesome stuff",
"icons": {
"60": "/img/icon-60.png",
"128": "/img/icon-128.png"
},
"fullscreen": true,
"orientations": ["portrait","landscape-secondary"],
"chrome": { "navigation": true},
"developer": {
"name": "Colin Frei",
"url": "http://colinfrei.com"
}
}
Firefox OS App Icon Guidelines
Let's expand on that a bit:
There's some more not really interesting data in here, including data about you, as well as an icon.
You can add a bunch of different icons in different sizes if you want, and for Firefox OS apps there are some guidelines to what icons should look like.
If you want to submit your app to the marketplace you'll need a 128x128 icon
There's also a fullscreen flag, which is specific to FirefoxOS, and allows you to say if the app should be launched in fullscreen mode or not.
Firefox Marketplace requires the developer name to be included.
Adding Chrome
"chrome": { "navigation": true}
The 'chrome' attribute allows you to have a navigation bar appear automatically.
Don't use this. Whenever possible, build a back button into your app directly.
Locales
{
"name": "Cool App",
"description": "This app lets you do really awesome stuff",
"default_locale": "en",
"locales": {
"de": {
"description": "Beschreibung der App"
}
}
}
We can add translations for pretty much all this metadata.
We specify the default_locale property, saying all this stuff is English, and then have a locales object, with an object for each language, where we can overwrite the fields above.
This does not have anything to do with the app's language itself, only the metadata in this manifest file.
You can overwrite almost all the fields in it though, with very few exceptions.
Web Activities
{
"name": "Cool App",
"description": "This app lets you do really awesome stuff",
"activities": {
"pick": {
"href": "./pick.html",
"disposition": "inline",
"filters": {
"type": ["image/*","image/jpeg","image/png"]
},
"returnValue": true
}
}
}
We talked about web activities previously, the configuration for these would go into the manifest file in the activities object.
Messages
{
"name": "Cool App",
"description": "This app lets you do really awesome stuff",
"messages": [
{ "telephony-new-call": "/dialer/index.html#keyboard-view" },
{ "notification": "/dialer/index.html#keyboard-view" },
{ "alarm": "/facebook/fb_sync.html" }
]
}
I'm not the expert on this, but in general messages allow you to catch system events and react to them. The whole thing is a two step process - you register for the event here in the manifest and also have to register the actual callback function in the JavaScript code.
This can be useful combined with the Alarm API as a 'local notification' system.
Market Restrictions
{
"name": "Cool App",
"description": "This app lets you do really awesome stuff",
"installs_allowed_from": [
"https://marketplace.firefox.com",
"http://colinfrei.com"
]
}
Permissions
{
"name": "App Name",
"description": "A description of your app",
"type": "privileged",
"permissions": {
"desktop-notification": {
"description": "Required to notify about stuff"
}
}
}
There are two interesting things here, the type, and the permissions. They're linked, but lets look at them one at a time.
Types
Web
Privileged
Certified
Hosted
✔
✖
✖
Packaged
✔
✔
✔
Types are important - they basically decide what APIs an app has access to. There are three permission types for Firefox OS: web, privileged and certified.
Web apps have the leat permission, but can be distributed in the easiest way.
Privileged apps have access to some APIs that can be "misused", so the code of these apps is reviewed to make sure it doesn't do anything stupid. One example is the Contacts API, which allows you to read the content of your phonebook.
Certified apps have access to all the APIs, so these are extremely limited. This includes things like being allowed to register to handle phone calls. These apps are reviewed for security, and digitally signed. Most apps that are certified are probably preloaded apps.
In addition to permission types there's what I call app types. These differentiate between apps that are hosted on the web and use appcache and stuff to work offline, and apps that are packaged as a zip file and installed directly on the phone.
The important distinction here is between privileged as a permission type and packaged as an app type. I spent about half a day trying to figure out why I didn't have permission to use an API, because I thought packaged automatically meant privileged, which it doesn't.
Permissions
alarms
audio-channel-*
contacts
desktop-notification
geolocation
storage
systemXHR
Full List
Permissions
{
"name": "App Name",
"description": "A description of your app",
"type": "privileged",
"permissions": {
"contacts": {
"description": "Required to match users",
"access": "readonly"
}
}
}
So again, the format for permissions is really easy, just add the permission and a description of what it's used for and you're done. Very few permissions require an additional level, this can be set using the access property.
This can be readonly, readwrite, readcreate or createonly
Versions & Updating
{
"name": "App Name",
"description": "A description of your app",
"version": "2.0"
}
Updating an app happens differently depending on what kind of app we're dealing with. Web Apps can be updated like normal, using the AppCache updating mechanisms.
Packaged apps can be updated by rebuilding the package and updating the version number in the manifest file.
Bits & Pieces
Content Type: application/x-web-app-manifest+json
Same Origin
Absolute Paths
So now we've got our manifest put together! Anything else we need to know?
Well one thing is that manifests need to be served with a content-type of 'application/x-web-app-manifest+json', so that they're recognized and parsed. Github does this automatically for files with a .webapp ending, you can output the content type easily on your own server as well.
Then, the manifest file has to be hosted at the same origin as your app. Seems obvious too, but is probably worth mentioning. Also, paths to icons and so on should be absolute.
There are manifest validators available, and there's also a service with an API that allows you to validate manifests if you happen to need something like that.
Firefox Marketplace
First off, notice that it's called Firefox Marketplace and not Firefox OS Marketplace.
That's because it's not meant only for Firefox OS, you can submit normal web apps, which can be installed through Firefox (like the Chrome Web Store), but also on Firefox on Android, a first step towards mobile apps. And of course you can allow them to be installed on Firefox OS.
Submitting your app
Submitting an app to the Firefox Marketplace works two ways, depending on the type of app you're trying to submit. You'll need to log in with a Persona account and then you'll see a screen where you can select for what device(s) your app is. If you're submitting a hosted web app you can simply enter the URL to your manifest file, and if you want to submit a packaged app you can upload the zip file for it.
The site validates the manifest, and in the case of packaged apps does some security checks on the code as well (CSP). Once those pass you can go to the next step where you can enter details about your app, things like categories, a privacy policy, ...
Review criteria
So there are guidelines to what's allowed in the store and what isn't, and there's a team that reviews all the submissions. Reviews are meant to be fair, and be mostly about security checks and providing feedback to developers.
Reviews are not QA! Reviewers will install the app and click around a bit and make sure the app seems to work and isn't misleading.
Once your app passes the review it'll show up on the marketplace.
You can review apps too!
Reviewer Application
Requirements:
Basic HTML Knowledge
Friendly and helpful attitude
Fluent in English