So you want to build a "web app"...



So you want to build a "web app"...

0 0


FirefoxOsAppDayZurichTalk

Slides for my talk at the Firefox OS App Day 2014 in Zurich

On Github colinfrei / FirefoxOsAppDayZurichTalk

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.

Web APIsthe fun part

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.

More Links

Overview of WebAPIsBoilerplate App (Example Code)Web API Blog Post

WebActivities

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.

More Links

Wiki DocumentationExample Code in Boilerplate AppWeb Activities Intro Blogpost

Open Web Appmanifest

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.

More Links

Firefox OS App Icon GuidelinesBlog Post about Manifests evolvingManifest specificationApp Permissions

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

More Links

Marketplace Review CriteriaBlog Post "Become a Marketplace Reviewer"

Thanks!