Building an Ionic Spotify App - Part 3: Native Spotify Integration Last update: 2018-08-07

Building an Ionic Spotify App - Part 3: Native Spotify Integration

Over the last months we had a series on using Spotify with Ionic and to conclude this series one more post follows that how to truly use the full power of Spotify by combining all previous elements.

Make sure that you have followed along the previous tutorials as this one is based on the second part.

Building an Ionic Spotify App - Part 1: OAuth Building an Ionic Spotify App - Part 2: App with Web API

Today we will use the Cordova Spotify plugin to play a complete song from Spotify and not only a short preview. We will use the finished code from the last part, add the plugin and change a few parts so let’s go!

Getting Started

First of all you need to add the plugin to your current project:

ionic cordova plugin add cordova-spotify

If you encounter problems during the installation, you might need to remove your platform, check the dependencies that are added to your package.json and see if you can find the issue. After a few attempts it finally worked for me although I didn’t really change anything.

The app should already work from the previous examples which means you can log in , retrieve the playlists of a user and also display the tracks of a playlist.

Storing the Access Token

In order to play our tracks in full length we need to pass the clientId and accessToken to the plugin once we call play(), therefore we need a way to pass this information to the next page.

Normally you would of course have a service that stores the information so your plugin can easily retrieve the dat, but for now we will just keep track of that data and pass it to the next playlist page. In your project you need to change the pages/home/home.ts to:

import { Component } from '@angular/core';
import { NavController, Platform, LoadingController, Loading } from 'ionic-angular';
import SpotifyWebApi from 'spotify-web-api-js';
import { Storage } from '@ionic/storage';

declare var cordova: any;

@Component({
  selector: 'page-home',
  templateUrl: 'home.html'
})
export class HomePage {
  result = {};
  spotifyApi = new SpotifyWebApi();
  loading: Loading;
  loggedIn = false;
  playlists = [];

  clientId = 'your-spotify-app-id';
  accessToken = null;

  constructor(public navCtrl: NavController, private storage: Storage, private plt: Platform, private loadingCtrl: LoadingController) {
    this.plt.ready().then(() => {
      this.storage.get('logged_in').then(res => {
        if (res) {
          this.authWithSpotify(true);
        }
      })
    });
  }

  authWithSpotify(showLoading = false) {
    const config = {
      clientId: this.clientId,
      redirectUrl: "devdacticspotify://callback",
      scopes: ["streaming", "playlist-read-private", "user-read-email", "user-read-private"],
      tokenExchangeUrl: "https://spotifyoauthserver.herokuapp.com/exchange",
      tokenRefreshUrl: "https://spotifyoauthserver.herokuapp.com/refresh",
    };

    if (showLoading) {
      this.loading = this.loadingCtrl.create();
      this.loading.present();
    }

    cordova.plugins.spotifyAuth.authorize(config)
    .then(({accessToken, encryptedRefreshToken, expiresAt }) => {
      if (this.loading) {
        this.loading.dismiss();
      }
      this.accessToken = accessToken;
      this.spotifyApi.setAccessToken(accessToken);
      this.loggedIn = true;
      this.storage.set('logged_in', true);
      this.getUserPlaylists();
    }, err => {
      if (this.loading) {
        this.loading.dismiss();
      }
    });
  }

  getUserPlaylists() {
    this.loading = this.loadingCtrl.create({
      content: 'Loading Playlists...'
    });
    this.loading.present();

    this.spotifyApi.getUserPlaylists()
    .then(data => {
      if (this.loading) {
        this.loading.dismiss();
      }
      this.playlists = data.items;
    }, err => {
      if (this.loading) {
        this.loading.dismiss();
      }
    });
  }

  openPlaylist(item) {
    this.navCtrl.push('PlaylistPage', { playlist: item, access: this.accessToken, clientId: this.clientId });
  }

  logout() {
    cordova.plugins.spotifyAuth.forget();

    this.loggedIn = false;
    this.playlists = [];
    this.storage.set('logged_in', false);
  }

}

It’s still mostly the same code and I’ve marked the changed lines for you.

Changing Our Playlist Logic

Now our page get’s the information we need, and we can simply call the Cordova plugin directly to access the functions. You can see an overview about the functions and return values here.

There are not so many features included, but we can play, pause, resume or skip to a position inside a track and we gonna add all of that. We also still have the logic from the Web API to retrieve the tracks, so we now combine both approaches inside our app!

We also keep track of the current playing state to show the right buttons at the right time. In comparison to the last part we also removed the Ionic Native Media plugin as we are now using the native audio through the plugin.

The changes are marked again so open your pages/playlist/playlist.ts to:

import { Component } from '@angular/core';
import { IonicPage, NavController, NavParams, LoadingController, Loading } from 'ionic-angular';
import SpotifyWebApi from 'spotify-web-api-js';

declare var cordova: any;

@IonicPage()
@Component({
  selector: 'page-playlist',
  templateUrl: 'playlist.html',
})
export class PlaylistPage {
  spotifyApi: any;
  loading: Loading;
  playlistInfo = null;
  tracks = [];

  playing = false
  paused = false;

  clientId = null;
  accessToken = null;

  constructor(public navCtrl: NavController, public navParams: NavParams,
    private loadingCtrl: LoadingController) {

    this.clientId = this.navParams.get('clientId');
    this.accessToken = this.navParams.get('access');

    let playlist = this.navParams.get('playlist');
    this.spotifyApi = new SpotifyWebApi();
    this.loadPlaylistData(playlist);
  }

  loadPlaylistData(playlist) {
    this.loading = this.loadingCtrl.create({
      content: 'Loading Tracks...'
    });
    this.loading.present();

    this.spotifyApi.getPlaylist(playlist.owner.id, playlist.id).then(data => {
      this.playlistInfo = data;
      this.tracks = data.tracks.items;
      if (this.loading) {
        this.loading.dismiss();
      }
    });
  }

  play(item) {
    cordova.plugins.spotify.play(item.track.uri, {
      clientId: this.clientId,
      token: this.accessToken
    })
      .then(() => {
        this.playing = true;
        this.paused = false;
      });
  }

  pause() {
    cordova.plugins.spotify.pause()
      .then(() => {
        this.playing = false;
        this.paused = true;
      });
  }

  resume()  {
    cordova.plugins.spotify.resume()
      .then(() => {
        this.playing = true;
        this.paused = false;
      });
  }

  seekTo() {
    cordova.plugins.spotify.seekTo(1000 * 120)
      .then(() => console.log(`Skipped forward`))
  }
}

The last missing piece is to change the logic of our view a bit and use all the functions we have added. Besides that, nothing really changed as we are still relying on the information from the Web API so go and finish it by changing your pages/playlist/playlist.html:

<ion-header>
  <ion-navbar>
    <ion-title *ngIf="playlistInfo">{{ playlistInfo.name }}</ion-title>
  </ion-navbar>
</ion-header>

<ion-content>

  <ion-list>
    <ion-card *ngFor="let item of tracks">
      <ion-item>
        <ion-avatar item-start>
          <img [src]="item.track.album.images[0].url">
        </ion-avatar>
        <h2>{{ item.track.name }}</h2>
        <p>{{ item.track.artists[0].name}}</p>
      </ion-item>

      <ion-row>
        <ion-col>
          <button ion-button icon-left (click)="play(item)" clear small>
            <ion-icon name="play"></ion-icon>
            Play
          </button>
        </ion-col>
        <ion-col *ngIf="playing && !paused">
          <button ion-button icon-left (click)="pause()" clear small>
            <ion-icon name="close"></ion-icon>
            Pause
          </button>
        </ion-col>
        <ion-col *ngIf="paused">
          <button ion-button icon-left (click)="resume()" clear small>
            <ion-icon name="open"></ion-icon>
            Resume
          </button>
        </ion-col>
        <ion-col>
          <button ion-button icon-left (click)="seekTo()" clear small>
            <ion-icon name="open"></ion-icon>
            Seek To
          </button>
        </ion-col>
      </ion-row>

    </ion-card>
  </ion-list>

</ion-content>

That’s it and now you are playing native Spotify music inside your Ionic app!

Conclusion

We already had a good app after the first 2 parts but now we can actually use everything from Spotify. However, I’m not 100% in how far the plugin is still maintained and what happens if new iOS or Android versions break the plugin.

Also, you might have noticed that we are using it as a pure Cordova plugin, so if you want to see it inside Ionic Native you might have to create a pull request for it!