You will need Node and Ionic installed on your machine. A basic knowledge of modern JavaScript development will be helpful.

In this tutorial, we are going to explore how to build a simple social application using Ionic and Pusher. Ionic will be used to create the mobile demo and Pusher Channels to add online functionality. At the end of this tutorial, you will have built an application that looks like this:

A while back on Twitter, the official Twitter handle teased their followers with a sample of an interesting idea they were exploring. They were exploring the possibility of letting you know who on your Twitter feed is online. This is something very different and it got a lot of mixed reactions because we know Twitter for for likes and retweets but not “online five minutes ago”. In other messaging applications we use, it is important to know if who you’re interacting with is online. As a developer, you are likely curious about how this all works.

Requirements

To be follow through this article properly, you’ll need the following:

Basic knowledge of JavaScript

Ionic installed on your machine, you can read up on how to do that here

Node.js installed on your machine

NPM installed on your machine

Getting started

To get started, create a new ionic application using the command:

ionic start twi-clone blank

This creates a starter ionic project in a folder titled twi-clone . To see the demo application at work, go to your terminal and run the command:

cd twi-clone ionic serve

This serves the Ionic application. Navigate your browser to http://locahost:8100 and you should get a view that looks like this:

Building the backend server

The backend server of our application will be responsible for doing the following:

Sharing users’ posts

Authenticating new users on the presence channel

Create a /server folder in your project:

mkdir server cd server

Install the Node modules that we will need to power the backend server:

npm install express body-parser pusher

express will power the web server

will power the web server body-parser to handle incoming requests

to handle incoming requests pusher to add realtime functionality and online presence

Afterwards, create a server.js file in the server/ directory:

touch server.js

Edit the server.js file to look as follows:

const express = require ( 'express' ) const bodyParser = require ( 'body-parser' ) const Pusher = require ( 'pusher' ); const app = express(); let pusher = new Pusher({ appId : 'PUSHER_APP_ID' , key : 'PUSHER_APP_KEY' , secret : 'PUSHER_APP_SECRET' , cluster : 'PUSHER_APP_CLUSTER' , encrypted : true }); app.use(bodyParser.json()); app.use(bodyParser.urlencoded({ extended : false })); app.use( ( req, res, next ) => { res.header( 'Access-Control-Allow-Origin' , '*' ); res.header( 'Access-Control-Allow-Headers' , 'Origin, X-Requested-With, Content-Type, Accept' ); next(); }); [...]

We include the necessary JavaScript libraries and then create a new Pusher object using your Pusher application credentials.

To obtain your Pusher credentials, create a new account here. Afterwards, you’ll be redirected to your Pusher dashboard. Go ahead and create a new project, obtain your PUSHER_APP_ID , PUSHER_APP_KEY , PUSHER_APP_SECRET , PUSHER_APP_CLUSTER and add them to your server.js file.

Afterwards, we specify some application middleware to handle incoming requests. The backend server will have two routes:

/pusher/auth - will be used to authenticate users joining the presence channel

- will be used to authenticate users joining the presence channel /create-post - will be used when a new post is created.

With Pusher, when a new client tries to join a presence channel, a POST request is first made to authenticate the new client. In this case, a random string is created to identify the client and this makes up the presenceData object. The presenceData , channel and socketId are then passed to Pusher to authenticate the client.

The /create-post route accepts the incoming data and then triggers a new-post event to the presence-channel .

In later parts of the article, we will see how the channel is created in our Ionic application

Add the code below to your server/server.js file:

[...] app.post( '/pusher/auth' , (req, res) => { let socketId = req.body.socket_id; let channel = req.body.channel_name; random_string = Math .random().toString( 36 ).replace( /[^a-z]+/g , '' ).substr( 0 , 5 ); let presenceData = { user_id : random_string, user_info : { username : '@' + random_string, } }; let auth = pusher.authenticate(socketId, channel, presenceData); res.send(auth); }); app.post( '/create-post' , (req, res) => { pusher.trigger( 'presence-channel' , 'new-post' , { 'username' : req.body.username, 'content' : req.body.content }) res.json({ 'status' : 200 }); }); let port = 3128 ; app.listen(port); console .log( 'listening' );

Now that the backend server is created, you can run it by entering the command:

node server.js

Now, let’s look at how to build the rest of the application.

Creating the interface

The starter application created a default homepage which we will then update to fit our particular use case. Update your home.html file to look like this:

< ion-header > < ion-navbar > < ion-title style = "text-align: center" > Let's Go Social </ ion-title > </ ion-navbar > </ ion-header > < ion-content padding > < form ( ngSubmit )= "submitPost()" > < div class = "compose-post-area" > < ion-textarea class = "post-compose" placeholder = "Post something...." [( ngModel )]= "post.content" name = "content" > </ ion-textarea > < button ion-button round class = "tweet-post" type = "submit" > POST </ button > </ div > </ form > < div > < ion-card > < ion-item > < h3 > Hi < i > @{{ current_user }} </ i > </ h3 > < h3 > Friends Online: {{ get_users_online() }} </ h3 > </ ion-item > </ ion-card > < div * ngFor = "let post of post_list" > < ion-card > < ion-item > < ion-avatar item-start > < img src = "https://api.adorable.io/avatars/100/avatar.png" alt = "" > </ ion-avatar > < p > @{{ post.username }} < i > {{ isOnline(post.username) }} </ i > </ p > </ ion-item > < ion-card-content > < p > {{ post.content }} </ p > </ ion-card-content > < ion-row > < ion-col > < button ion-button ion-start clear small > < ion-icon name = "repeat" > </ ion-icon > < div > 932 reposts </ div > </ button > </ ion-col > < ion-col > < button ion-button ion-start clear small color = "danger" > < ion-icon name = "heart" > </ ion-icon > < div > 12k likes </ div > </ button > </ ion-col > </ ion-row > </ ion-card > </ div > </ div > </ ion-content >

The page also has the following styling:

# src/page/home/home.scss .compose-post-area{ display: flex; align-items: center; } ion-content{ background-color: #FAFAFA; }

We have seen the interface of the application. To add any functionality to our application, we need to edit the home.ts file to look like this:

import { Component } from '@angular/core' ; import { NavController, AlertController } from 'ionic-angular' ; @Component({ selector : 'page-home' , templateUrl : 'home.html' }) export class HomePage { post: any = {}; presence_channel: any; current_user; users_online = {}; post_list = [ { 'username' : 'og' , 'content' : 'Making money was the plan oooo' }, { 'username' : 'daddywon' , 'content' : 'You can catch me on the express' } ]; constructor (public navCtrl: NavController) { [...] } [...] }

We can see that the home page component contains the variables that were referenced in the homepage template.

Creating the Pusher service

To know the number of friends online and when someone who shares a post is online, let’s make use of Presence Channels by Pusher. To use Pusher in our Ionic application, we need to install the library using the command:

npm install pusher-js

Now, let’s create a simple Pusher provider. To do this, head over to your terminal and run the command:

ionic generate provider pusher-service

Now, a pusher-service provider has been created. In the pusher-service.ts create a new Pusher object in the constructor by specifying the PUSHER_APP_KEY , PUSHER_APP_CLUSTER and the authEndpoint created on our backend server earlier in the article.

Afterwards, subscribe to the presence-channel . The init() function is then responsible for returning the created presence channel which is to be used in our home.ts file. Update your pusher-service.ts to look like this:

import { HttpClient } from '@angular/common/http' ; import { Injectable } from '@angular/core' ; import Pusher from 'pusher-js' ; @Injectable() export class PusherServiceProvider { presenceChannel; constructor (public http: HttpClient) { let pusher = new Pusher( 'PUSHER_APP_KEY' , { authEndpoint : 'http://localhost:3128/pusher/auth' , cluster : 'PUSHER_APP_CLUSTER' }); this .presenceChannel = pusher.subscribe( 'presence-channel' ); } public init() { return this .presenceChannel; } }

Update your app.module.ts to ensure that the Pusher provider and the HttpClientModule are loaded:

import { BrowserModule } from '@angular/platform-browser' ; import { ErrorHandler, NgModule } from '@angular/core' ; import { IonicApp, IonicErrorHandler, IonicModule } from 'ionic-angular' ; import { SplashScreen } from '@ionic-native/splash-screen' ; import { StatusBar } from '@ionic-native/status-bar' ; import { MyApp } from './app.component' ; import { HomePage } from '../pages/home/home' ; import { PusherServiceProvider } from '../providers/pusher-service/pusher-service' ; import { HttpClientModule } from '@angular/common/http' ; @NgModule({ declarations : [ MyApp, HomePage ], imports : [ BrowserModule, HttpClientModule, IonicModule.forRoot(MyApp) ], bootstrap : [IonicApp], entryComponents : [ MyApp, HomePage ], providers : [ StatusBar, SplashScreen, { provide : ErrorHandler, useClass : IonicErrorHandler }, PusherServiceProvider ] }) export class AppModule { }

Now, let’s go back to the home page component.

Adding realtime functionality and online presence with Pusher

We are going to update the home.ts file to allow users know when other users are online. We first get the Pusher Channel using the Pusher provider we created earlier and then listen for the pusher:subscription_succeeded event. Once the client has successfully subscribed to the presence-channel , a members object is returned that contains information about the people subscribed to the presence-channel .

We also listen for a new-post event on the presence-channel . When a new-post event is triggered, the post_list is then updated to contain the new post. At this point, your home.ts should look like this:

import { Component } from '@angular/core' ; import { NavController, AlertController } from 'ionic-angular' ; import { PusherServiceProvider } from '../../providers/pusher-service/pusher-service' ; import { HttpClient } from '@angular/common/http' ; @Component({ selector : 'page-home' , templateUrl : 'home.html' }) export class HomePage { post: any = {}; presence_channel: any; current_user; users_online = { }; post_list = [ { 'username' : 'og' , 'content' : 'Making money was the plan oooo' }, { 'username' : 'daddywon' , 'content' : 'You can catch me on the express' } ]; constructor (public navCtrl: NavController, private pusher: PusherServiceProvider, private http: HttpClient, public alertCtrl: AlertController) { let self = this this .presence_channel = this .pusher.init(); this .presence_channel.bind( 'pusher:subscription_succeeded' , function ( members ) { console .log(members); self.users_online = members.members; self.current_user = members.myID; }) this .presence_channel.bind( 'new-post' , function ( body ) { self.post_list.unshift(body); }) } [...] }

Finally, we have three other methods in the component:

get_users_online() which returns the number of users that are currently online

which returns the number of users that are currently online isOnline() that checks if a particular user is currently online

that checks if a particular user is currently online submitPost() that submits a post by a user

Add the methods to your home.ts file:

[...] get_users_online() { return Object .keys( this .users_online).length - 1 ; } isOnline(username: string) { if (username in this .users_online) { return 'online' } else { return 'offline' } } submitPost() { let self = this ; let body = { 'username' : this .current_user, 'content' : this .post.content } const alert = this .alertCtrl.create({ title : 'Post Shared!' , subTitle : `Users online to see your post: ${self.get_users_online()} ` , buttons : [ 'OK' ] }); this .http.post( 'http://localhost:3128/create-post' , body).subscribe( () => { alert.present(); }); } }

Recall in the home.html interface, we had the following form:

< form ( ngSubmit )= "submitPost()" > < div class = "compose-post-area" > < ion-textarea class = "post-compose" placeholder = "Post something...." [( ngModel )]= "post.content" name = "content" > </ ion-textarea > < button ion-button round class = "tweet-post" type = "submit" > POST </ button > </ div > </ form >

When the POST button is clicked, the submitPost() function is called and the post content and username of the current user are sent to the /create-post of the backend server which then triggers the new-post event on the presence-channel and the post_list is updated accordingly.

To see it all at work, serve your Ionic application using the command:

npm start # or ionic serve

Ensure your backend server is on.

Navigate on your browser to http://localhost:8100 and you should get an experience that looks like this:

Testing your Ionic application on a mobile device

If you’re building with Ionic, you generally don’t intend to have your application on your browser. Rather, you’d want to have it running on mobile devices. Before you do that, you should serve the backend of your application on a tunnel using ngrok. Head over here and follow the download instructions for your platform.

After you’ve had it installed, tunnel your backend server using the command:

ngrok http 3128

You should get a view that looks like this:

ngrok by @inconshreveable (Ctrl+C to quit) Session Status online Session Expires 7 hours, 59 minutes Version 2.2.8 Region United States (us) Web Interface http://127.0.0.1:4040 Forwarding http://b3b88c11.ngrok.io -> localhost:3128 Forwarding https://b3b88c11.ngrok.io -> localhost:3128 Connections ttl opn rt1 rt5 p50 p90 0 0 0.00 0.00 0.00 0.00

This means that you can now access your backend server using the public URL → https://b3b88c11.ngrok.io

Update the authEndpoint of your pusher-service.ts to use the new public URL:

[...] let pusher = new Pusher( '9effdb6e1245bda33b17' , { authEndpoint : 'https://b3b88c11.ngrok.io/pusher/auth' , cluster : 'mt1' }); [...]

Also update the POST request in the submitPost of your home.ts file:

[...] this .http.post( 'http://localhost:3128/create-post' , body).subscribe( () => { alert.present(); }); [...]

To test the application on your mobile device, download the IonicDevApp on your mobile device. Ensure that your device and computer are on the same network and you should see this demo application listed there:

Now, testing with the device, we get the following:

Conclusion

In this tutorial, we saw how to use Pusher Channels and Ionic to build a social media application that lets you know who’s online and how many online friends your post reach when they’re shared. There are many more use cases for Pusher Channels, so feel free to use concepts shared here in your own application. Here’s a link to the GitHub repository.