![]() |
Hybrid apps are gaining popularity. The Gartner research firm predicts that by 2016, hybrid apps will constitute the majority of mobile apps. So it is good time to learn some modern technologies for building hybrid apps.
In this tutorial we are going to see how it is possible to build Cordova application with push notifications based on the Ionic Framework and ngCordova created by the Ionic team.
Ionic uses AngularJS, so it is assumed you are familiar with it.
What are we building in this tutorial?
Unfortunately, there are a lot of bad things happening every day. So we are going to create an iOS application to show some good news. We will also create own server for sending push notifications using apn, based on Node.js and MongoDB with a simple form to create a new post.
The name of the application will be PushNews, but of course you can use your own.
Prerequisites
For deploying we will use Heroku and Mongolab, so you need to have accounts there or you can use your own hosting instead.
Install Ionic
Now we should install/update CLI for Cordova and Ionic, to have an opportunity to create new apps, add cordova plugins, etc.
Run this command in your Terminal:
- $ npm install -g ionic cordova
Creating Ionic app
For creating and serving basic Ionic app with tabs starter template, run
- $ ionic start pushNews tabs
- $ cd pushNews
- $ ionic serve
![]() |
- $ bower install --save ngCordova angular-local-storage
- <!-- index.html -->
- ...
- <script src="lib/angular-local-storage/dist/angular-local-storage.min.js"></script>
- <script src="lib/ngCordova/dist/ng-cordova.min.js"></script>
- <script src="cordova.js"></script>
- ...
- // www/js/app.js
- ...
- angular.module('app', ['ngCordova', 'LocalStorageModule'])
- ...
- Login page
- All news list
- News view page
- User profile page
- // www/js/app.js
- ...
- .config(function ($stateProvider, $urlRouterProvider) {
- $urlRouterProvider.otherwise('/login');
- $stateProvider
- .state('login', {
- url: '/login',
- templateUrl: 'templates/login.html',
- controller: 'LoginCtrl'
- })
- .state('tab', {
- abstract: true,
- templateUrl: "templates/tabs.html"
- })
- .state('tab.news', {
- url: '/news',
- views: {
- 'tab-news': {
- templateUrl: 'templates/tab-news.html',
- controller: 'NewsCtrl'
- }
- }
- })
- .state('tab.details', {
- url: '/news/:id',
- views: {
- 'tab-news': {
- templateUrl: 'templates/details.html',
- controller: 'DetailsCtrl'
- }
- }
- })
- .state('tab.profile', {
- url: '/profile',
- views: {
- 'tab-profile': {
- templateUrl: 'templates/tab-profile.html',
- controller: 'ProfileCtrl'
- }
- }
- });
- });
Let's create Express server. I will use Heroku to host it. Follow official Getting Started if you are not familar with Heroku. As an alternative you can use for development your localhost with ngrok.
- // server.js
- var express = require('express');
- var app = express();
- var bodyParser = require('body-parser');
- require('./config')(app);
- require('./models')(app);
- var config = app.get('config');
- app.use(function (req, res, next) {
- res.header('Access-Control-Allow-Credentials', true);
- res.header('Access-Control-Allow-Origin', req.headers.origin);
- res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE');
- res.header(
- 'Access-Control-Allow-Headers',
- 'X-Requested-With, X-HTTP-Method-Override, Content-Type, Accept'
- );
- if ('OPTIONS' === req.method) {
- res.status(200).end();
- } else {
- next();
- }
- });
- app.use(bodyParser.json());
- app.use(bodyParser.urlencoded({
- extended: true
- }));
- var router = express.Router();
- app.set('router', router);
- app.use(router);
- var http = require('http');
- http.createServer(app)
- .listen(config.PORT, function () {
- console.log('app start on port ' + config.PORT);
- });
- require('./routes')(app);
Authorization and Push Notifications
There are multiple ways to manage auth process in Ionic apps. We will use the next one: user login with twitter, then we store returned data in localStorage and remove it on logout. If user exists in localStorage he is logged in, if not, we will redirect him to login page. Don't forget to add CSRF or other protection for the real app.
Login with Twitter
To use twitter we must also add sha1 library:
- $ bower install --save jsSHA
- <!-- www/index.html -->
- ...
- <script src="lib/jsSHA/src/sha1.js"></script>
- ...
- $ cordova plugin add https://git-wip-us.apache.org/repos/asf/cordova-plugin-inappbrowser.git
A little earlier we have set login.html as template for the login state, let's create it:
- <!-- www/templates/login.html -->
- <div class="row row-center">
- <div class="col">
- <h2 class="text-center">Good News</h2>
- <div class="padding-top">
- <button class="button button-block button-calm" ng-click="twitter()">
- <i class="icon ion-social-twitter"></i> Login with Twitter
- </button>
- </div>
- </div>
- </div>
It should be noticed, that any cordova plugin call should be wraped with the deviceready event. Or use $ionicPlatform.ready() available in Ionic.
- // www/js/controllers.js
- ...
- .controller('LoginCtrl', function ($scope, $state, $cordovaOauth,
- UserService, Config, $ionicPlatform,
- $ionicLoading, $cordovaPush) {
- if (UserService.current()) {
- $state.go('tab.news');
- }
- $scope.twitter = function () {
- $ionicPlatform.ready(function () {
- $cordovaOauth.twitter(Config.twitterKey, Config.twitterSecret)
- .then(function (result) {
- $ionicLoading.show({
- template: 'Loading...'
- });
- UserService.login(result).then(function (user) {
- if (user.deviceToken) {
- $ionicLoading.hide();
- $state.go('tab.news');
- return;
- }
- $ionicPlatform.ready(function () {
- $cordovaPush.register({
- badge: true,
- sound: true,
- alert: true
- }).then(function (result) {
- UserService.registerDevice({
- user: user,
- token: result
- }).then(function () {
- $ionicLoading.hide();
- $state.go('tab.news');
- }, function (err) {
- console.log(err);
- });
- }, function (err) {
- console.log('reg device error', err);
- });
- });
- });
- }, function (error) {
- console.log('error', error);
- });
- });
- };
- })
- ...
To send push notification to our users, we must know device token. We will receive it using $cordovaPush.register() function. Cordova plugin should be installed:
- $ cordova plugin add https://github.com/phonegap-build/PushPlugin.git
- // www/js/userService.js
- (function () {
- function _UserService($q, config, $http, localStorageService, $state) {
- var user;
- function loginUser(post) {
- var deferred = $q.defer();
- $http.post(config.server + '/user/login', post)
- .success(function (data) {
- if (data.error || !data.user) {
- deferred.reject(data.error);
- }
- localStorageService.set('user', data.user);
- user = data.user;
- deferred.resolve(data.user);
- })
- .error(function () {
- deferred.reject('error');
- });
- return deferred.promise;
- }
- function logoutUser() {
- localStorageService.remove('user');
- user = null;
- $state.go('login');
- }
- function currentUser() {
- if (!user) {
- user = localStorageService.get('user');
- }
- return user;
- }
- function registerDevice(putData) {
- var deferred = $q.defer();
- $http.put(config.server + '/user/registerDevice', putData)
- .success(function (data) {
- if (data.error || !data.user) {
- deferred.reject(data.error);
- }
- localStorageService.set('user', data.user);
- user = data.user;
- deferred.resolve(data.user);
- })
- .error(function () {
- deferred.reject('error');
- });
- return deferred.promise;
- }
- return {
- login: loginUser,
- logout: logoutUser,
- current: currentUser,
- registerDevice: registerDevice
- };
- }
- function _ConfigService() {
- return {
- server: 'http://push-news.herokuapp.com',
- twitterKey: 'your_twitter_key',
- twitterSecret: 'your_twitter_secret'
- };
- }
- _UserService.$inject = [
- '$q', 'Config', '$http', 'localStorageService',
- '$state', '$cordovaPush', '$ionicPlatform'
- ];
- angular.module('app.services')
- .factory('UserService', _UserService)
- .service('Config', _ConfigService);
- })();
- <!-- index.html -->
- ...
- <script src="js/userService.js"></script>
- ...
We should add user model and routes for /user/login and /user/registerDevice on our server
- // models/users.js
- var mongoose = require('mongoose');
- var Schema = mongoose.Schema;
- module.exports = function () {
- 'use strict';
- var UsersSchema = new Schema({
- socialId: {type: String, index: true, unique: true},
- username: {type: String, unique: true},
- deviceToken: {type: String, unique: true},
- deviceRegistered: {type: Boolean, default: false},
- createdAt: {type: Date, default: Date.now}
- });
- mongoose.model('Users', UsersSchema, 'Users');
- };
-----------------------------------------------------------------------------
- // models/users.js
- var mongoose = require('mongoose');
- var Schema = mongoose.Schema;
- module.exports = function () {
- 'use strict';
- var UsersSchema = new Schema({
- socialId: {type: String, index: true, unique: true},
- username: {type: String, unique: true},
- deviceToken: {type: String, unique: true},
- deviceRegistered: {type: Boolean, default: false},
- createdAt: {type: Date, default: Date.now}
- });
- mongoose.model('Users', UsersSchema, 'Users');
- };
---------------------------------------------------------------------------
- // routes/user.js
- var mongoose = require('mongoose');
- var Users = mongoose.model('Users');
- module.exports = function (app) {
- 'use strict';
- var router = app.get('router');
- router.post('/user/login', function (req, res) {
- var socialId = req.body.user_id;
- var username = req.body.screen_name;
- Users.findOne({socialId: socialId}).lean().exec(function (err, user) {
- if (err) {
- return res.json({error: err});
- }
- if (user) {
- return res.json({error: null, user: user});
- }
- var newUser = new Users({
- socialId: socialId,
- username: username
- });
- newUser.save(function (err, user) {
- if (err) {
- return res.json({error: err});
- }
- res.json({user: user, error: null});
- });
- });
- });
- router.put('/user/registerDevice', function (req, res) {
- var user = req.body.user;
- var token = req.body.token;
- Users.findByIdAndUpdate(user._id, {
- $set: {
- deviceToken: token,
- deviceRegistered: true
- }
- }).lean().exec(function (err, user) {
- if (err || !user) {
- return res.json({error: err});
- }
- res.json({error: null, user: user});
- });
- });
- };
If you found this post interesting, follow and support us.
Suggest for you:


No comments:
Post a Comment