Building an Ionic Twitter App With Timeline and Tweets Last update: 2017-05-23

Building an Ionic Twitter App With Timeline and Tweets

Building an Ionic Twitter App is really interesting, and from the number of responses and the times how often I’ve written a Twitter post I’m pretty sure this updated one for Ionic 3+ is really needed.

Inside this tutorial we will build an Ionic app with Twitter login, and after login we use our OAuth keys to make signed requests to the Twitter REST API through a nice NPM package called ng2-twitter.

For this tutorial you need a Twitter App and also Fabric account, but both is free to create and only takes minutes!

Once we are done you will be able to login, show a users timeline and also send out tweets. twitter-compose

Prerequisite

We’ll need some API keys for Twitter and and additional service, so let’s create them now.

First, you need a Twitter App because otherwise you won’t get any token and won’t be alowed to make specific calls like we want to. Go ahead and create an app; there’s nothing special you have to insert into those fields.

Make sure to have your Access Level set to at least Read and Write, but if you also add the permission for “direct messages” you’ll get the most power through the API later.

The next step is to get a key from Fabric. As the Twitter connect plugin states, there are 3 steps:

  1. Login to your Fabric account (or create one, if you don’t have one) and go to the Twitter install guide.
  2. Find the AndroidManifest.xml code block on that page.
  3. Find the API Key inside that code block.

The block should look like this:

// AndroidManifest.xml
<meta-data
    android:name="io.fabric.ApiKey"
    android:value="FABRIC_KEY"
/>

It’s not very hard to find these keys, so now you should have both your Twitter App keys and Fabric keys ready and at hand. You are ready to add the Twitter connect plugin to your app!

Setting up our Ionic Twitter App

We create a blank new Ionic app like always. To make use of Twitter login we install the according Twitter Connect plugin from Ionic Native as well as the in app browser to open tweet URLs later and of course we install ng2-twitter for our requests later.

When adding the Cordova plugins, we need to pass the Fabric API key from the first step of our tutorial to the plugin, so make sure you add the right key!

Finally we also create new pages and a provider for our app, so go ahead now and run:

ionic start devdacticTwitter blank
cd devdacticTwitter

npm install --save @ionic-native/in-app-browser
npm install --save @ionic-native/twitter-connect
npm install --save ng2-twitter

ionic cordova plugin add twitter-connect-plugin --variable FABRIC_KEY=fabric_API_key
ionic cordova plugin add cordova-plugin-inappbrowser

ionic g page login
ionic g page timeline
ionic g provider twitter

To hook up everything correctly we need to import the new stuff into our src/app/app.module.ts and add it to the array of providers like this:

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 { HttpModule } from '@angular/http';

import { InAppBrowser } from '@ionic-native/in-app-browser';
import { TwitterConnect } from '@ionic-native/twitter-connect';
import { TwitterService } from 'ng2-twitter';
import { TwitterProvider } from '../providers/twitter/twitter';

@NgModule({
  declarations: [
    MyApp
  ],
  imports: [
    BrowserModule,
    HttpModule,
    IonicModule.forRoot(MyApp)
  ],
  bootstrap: [IonicApp],
  entryComponents: [
    MyApp
  ],
  providers: [
    StatusBar,
    SplashScreen,
    TwitterConnect,
    TwitterService,
    InAppBrowser,
    TwitterProvider,
    {provide: ErrorHandler, useClass: IonicErrorHandler},
  ]
})
export class AppModule {}

For the last step you need the consumer key and secret key from your Twitter app, because they need to be added to your config.xml. You can find the values inside your Twitter app inside Keys and Access Tokens -> Application Settings. Go ahead and add these two entries inside the config.xml inside the widget block:

<widget>
  // ....
  <preference name="TwitterConsumerKey" value="<Twitter Consumer Key>" />
  <preference name="TwitterConsumerSecret" value="<Twitter Consumer Secret>" />
</widget>

Take a deep breath for one last step.. It’s just a tiny one.

We need to change the entry point of our app to our new LoginPage, therefore go to your src/app/app.component.ts and change it to:

import { Component } from '@angular/core';
import { Platform } from 'ionic-angular';
import { StatusBar } from '@ionic-native/status-bar';
import { SplashScreen } from '@ionic-native/splash-screen';

@Component({
  templateUrl: 'app.html'
})
export class MyApp {
  rootPage:any = 'LoginPage';

  constructor(platform: Platform, statusBar: StatusBar, splashScreen: SplashScreen) {
    platform.ready().then(() => {
      statusBar.styleDefault();
      splashScreen.hide();
    });
  }
}

I think we are finally ready to see some code, right?

give-code-soon

Making HTTP calls to Twitter REST

When we login through Twitter Connect we will receive a token and tokenSecret back after a successful login. These tokesn are needed to make authorized REST calls later through our provider which we will implement next.

The provider needs these tokens, so we allow to set them from outside (what we will use after user login) and we also need again our consumer Key and scret of our Twitter app. Of course here in the code is not the best place, I’m still wondering where it would be the safest to have these keys..

If you have an idea where it’s most safe to keep them please post below the article!

For now we go ahead and add 2 functions which are postTweet() to simply submit a tweet (yes, this is possible with the right access!) and also getHomeTimeline() to load the timeline of the current user.

These functions make use of our ng2-twitter, because otherwise we would need a lot of work here with different keys and signing objects like in my previous post on the Ionic Blog.

But now things work a bit smoother and we can pass in the needed variables to the calls of the Twitter REST API like the status text or the tweet count.

Go ahead and implement the src/providers/twitter/twitter.ts like this:

import { TwitterService } from 'ng2-twitter';
import { Injectable } from '@angular/core';
import 'rxjs/add/operator/map';

@Injectable()
export class TwitterProvider {
  token = null;
  tokenSecret = null;
  consumerKey = 'YOURCONSUMERKEY';
  consumerSecret = 'YOURCONSUMERSECRET';

  constructor(private twitter: TwitterService) { }

  setTokens(token, tokenSecret) {
    this.token = token;
    this.tokenSecret = tokenSecret;
  }

  postTweet(text) {
    return this.twitter.post(
      'https://api.twitter.com/1.1/statuses/update.json',
      {
        status: text
      },
      {
        consumerKey: this.consumerKey,
        consumerSecret: this.consumerSecret
      },
      {
        token: this.token,
        tokenSecret: this.tokenSecret
      }
    )
      .map(res => res.json());
  }

  getHomeTimeline() {
    return this.twitter.get(
      'https://api.twitter.com/1.1/statuses/home_timeline.json',
      {
        count: 10
      },
      {
        consumerKey: this.consumerKey,
        consumerSecret: this.consumerSecret
      },
      {
        token: this.token,
        tokenSecret: this.tokenSecret
      }
    )
      .map(res => res.json());
  };

}

Here would also be the place to add your own functions and more calls to Twitter, there is of course a lot more you can do besides receiving some tweets and posting stuff!

Building Twitter Login

We now get to an even more interesting part…the login!

The view will only consist of a button, to make this more beautiful is left as an exercise for the reader. Go ahead and change your src/app/pages/login/login.html to:

<ion-header>
  <ion-navbar color="primary">
    <ion-title>
      Devdactic + Twitter
    </ion-title>
  </ion-navbar>
</ion-header>

<ion-content padding>
  <button ion-button full (click)="loginWithTwitter()">Login with Twitter</button>
</ion-content>

The next step is the actual login process. After having setup all these keys everywhere there’s not really much we have to do here. We also add a few loading and alert notifications just to make the process more smooth.

Once we got a successful result we setup our own provider with the right tokens and continue to the TimelinePage of our app.

You can change your src/app/pages/login/login.ts to:

import { TwitterProvider } from './../../providers/twitter/twitter';
import { TwitterConnect } from '@ionic-native/twitter-connect';
import { Component } from '@angular/core';
import { IonicPage, NavController, AlertController, LoadingController, Loading } from 'ionic-angular';

@IonicPage()
@Component({
  selector: 'page-login',
  templateUrl: 'login.html',
})
export class LoginPage {
  loading: Loading;

  constructor(public navCtrl: NavController, private twitter: TwitterConnect, private twitterProvider: TwitterProvider, private alertCtrl: AlertController, private loadingCtrl: LoadingController) { }

  public loginWithTwitter() {
    this.showLoading();
    this.twitter.login().then((data) => {
      this.twitterProvider.setTokens(data.token, data.secret);
      this.loading.dismiss().then(() => {
        this.navCtrl.setRoot('TimelinePage');
      });
    }, error => {
      this.showError(error);
    });
  }

  private showLoading() {
    this.loading = this.loadingCtrl.create({
      content: 'Please wait...'
    });
    this.loading.present();
  }

  private showError(text) {
    this.loading.dismiss().then(() => {
      let alert = this.alertCtrl.create({
        title: 'Fail',
        message: text + '\nMake sure to setup Twitter account on your device.',
        buttons: ['OK']
      });
      alert.present(prompt);
    });
  }

}

As you can see, it’s really not that much in that controller, which is a good sign. Next stop: Timeline!

Showing Timeline and sending Tweets

I guess this part is what you waited for all the time, right?

As we have put most of the complex stuff into our provider we can now simply reap the fruits of our hard work. Our array of tweets is an Observable which will be filled from our provider after the page is loaded.

We can also trigger this loadTimeline() from a pull to refresh which we will add to our HTML in the next step. And that’s actually already everything here we need to receive the current timeline!

When we want to create a new tweet we show a simple alert view with an input for the message. Once the user sends this message we again make use of our TwitterProvider and use the send function.

Additional we have 2 helper functions to show the correct date of a tweet and also to open an URL of a Tweet.

Go ahead and add all of this now to your src/pages/timeline/timeline.ts:

import { TwitterProvider } from './../../providers/twitter/twitter';
import { Component } from '@angular/core';
import { IonicPage, NavController, Loading, LoadingController, AlertController, ToastController } from 'ionic-angular';
import { Observable } from 'rxjs/Observable';
import { InAppBrowser } from '@ionic-native/in-app-browser';

@IonicPage()
@Component({
  selector: 'page-timeline',
  templateUrl: 'timeline.html',
})
export class TimelinePage {
  tweets: Observable<any[]>;
  loading: Loading;

  constructor(public navCtrl: NavController, private twitterProvider: TwitterProvider, private alertCtrl: AlertController, private loadingCtrl: LoadingController, private toastCtrl: ToastController, private iab: InAppBrowser) {
  }

  ionViewWillEnter() {
    this.loadTimeline();
  }

  public loadTimeline(refresher?) {
    this.showLoading();
    this.tweets = this.twitterProvider.getHomeTimeline();
    this.tweets.subscribe(data => {
      this.loading.dismiss();
      refresher.complete();
    }, err => {
      refresher.complete();
      this.showError(err);
    });
  }
  public composeTweet() {
    let prompt = this.alertCtrl.create({
      title: 'New Tweet',
      message: "Write your Tweet message below",
      inputs: [
        {
          name: 'text'
        },
      ],
      buttons: [
        {
          text: 'Cancel'
        },
        {
          text: 'Tweet',
          handler: data => {
            this.postTweet(data.text);
          }
        }
      ]
    });
    prompt.present();
  }
  public dateForTweet(dateString) {
    let d = new Date(Date.parse(dateString));

    // http://stackoverflow.com/questions/3552461/how-to-format-a-javascript-date
    var datestring = ("0" + d.getDate()).slice(-2) + "-" + ("0" + (d.getMonth() + 1)).slice(-2) + "-" +
      d.getFullYear() + " " + ("0" + d.getHours()).slice(-2) + ":" + ("0" + d.getMinutes()).slice(-2);

    return datestring;
  }

  public openLinkUrl(url) {
    let browser = this.iab.create(url, 'blank');
  }

  public postTweet(text) {
    this.showLoading();
    this.twitterProvider.postTweet(text).subscribe(res => {
      this.loading.dismiss();
      let toast = this.toastCtrl.create({
        message: 'Tweet posted!',
        duration: 3000
      });
      toast.present();
    }, err => {
      this.showError(err);
    });
  }
  private showLoading() {
    this.loading = this.loadingCtrl.create({
      content: 'Please wait...'
    });
    this.loading.present();
  }

  private showError(text) {
    this.loading.dismiss();
    let alert = this.alertCtrl.create({
      title: 'Error',
      message: text,
      buttons: ['OK']
    });
    alert.present(prompt);
  }

}

If you have any questions to the code above simply let me know. It’s a big block of code but I think after doing all the stuff before everything should be more or less clear. If not, you know what to do.

We come to the end and have to compose the UI for our timeline by pulling out different properties of a tweet object.

It’s best when you log out the object and take a look at it yourself to see what everything you got there.

In our example we build some nice cards with the user image, name, tweet, possible tweet image and also link to a URL which will be opened with the in app browser.

Go ahead and finish this tutorial by replacing everything inside src/pages/timeline/timeline.html with this:

<ion-header>
  <ion-navbar color="primary">
    <ion-title>
      My Feed
    </ion-title>
    <ion-buttons end>
      <button ion-button icon-only (click)="composeTweet()">
        <ion-icon name="create"></ion-icon>
      </button>
    </ion-buttons>
  </ion-navbar>
</ion-header>

<ion-content>
  <ion-refresher (ionRefresh)="loadTimeline($event)">
    <ion-refresher-content></ion-refresher-content>
  </ion-refresher>

  <ion-card *ngFor="let tweet of tweets | async">

    <ion-item>
      <ion-avatar item-left>
        <img src={{tweet.user.profile_image_url}}>
      </ion-avatar>
      <h2>{{tweet.user.name}}</h2>
      <p>{{dateForTweet(tweet.created_at)}}</p>
    </ion-item>

    <img src={{tweet.extended_entities.media[0].media_url}} *ngIf="tweet.extended_entities">

    <ion-card-content>
      <p>{{tweet.text}}</p>
    </ion-card-content>

    <ion-row>
      <ion-col *ngIf="tweet.entities.urls.length > 0">
        <button ion-button clear small (click)="openLinkUrl(tweet.entities.urls[0].url)">
          <ion-icon name="open"></ion-icon>
          <div>Open Link</div>
        </button>
      </ion-col>
    </ion-row>

  </ion-card>

</ion-content>

Now everything is done and you can use your own little Twitter client, just like in the image at the start of this article (but of course with different tweets).

Recap

The beginning and configuration phase was a bit tough and dry, but once you got that right there’s nothing that can stop you from using the Twitter REST API!

Being able to pull the data from twitter on your own is somehow a cool feeling, perhaps you are already working on an app with Twitter integration?

Let us know below and leave a comment if you’ve read until here!

Happy Coding, Simon