How to connect Spotify SDK to your project
OUR Blog
iOS developer
Viktor Olesenko
iOS developer
MOBILE
Jun 16 2016

How to connect Spotify SDK to your project

Our company specializes in web development on Python and Django and mobile development for iOS and Android for more than 5 years. During this time we have realized plenty of projects with different orientation: entertainment, education, music, e-commerce, etc. and gained considerable experience which we are eager to share. The present article provides an instruction for connection of music streaming service Spotify. We will describe in details SDK connection stages, login process, token authentication and work with built-in Spotify player.

Spotify music streaming service is quite widespread in the world and its developers offer the opportunity to integrate it into your application with the help of SDK (currently version beta 16).

Official documentation contains a tutorial, but it only allows to get familiar with service’s basic functionality. Connection to a real project significantly differs in terms of login logic and work with tokens.

Stages of Spotify SDK integration into application specially designed for music streaming from several services are described below. Here we have considered the situation when user could link and unfasten their Spotify account and carry out the login from several devices without reconnection to music services.

Stages of Spotify integration into your application:

Before starting you have to create XCode project and register Spotify account. It is also necessary to activate premium account for streaming availability (if you are not sure in the expedience of such purchase - you can use free trial).

1. Spotify application registration

Go to developer.spotify.com/my-applications and log in under your account. Now find this button button create an app . Enter your name and app description. After this we are interested in the following fields:

  • Client ID - a unique identifier of our application. It is required for SDK integration.

  • Client Secret - a secret key to your application. It is used on the server (details below)

  • Redirect URL - a link to return to your application in case of login readdressing to a native application or Safari. We used this one projectname://spotify/callback

  • Bundle ID - project identifier known to every iOS developer. Naturally, specify/enter bundle ID for our application.

2. SpotifyLoginController creation

Login will be carried out not as it is specified in the tutorial, but by means of a customized solution. The reason for this is the necessity of receiving the authorization code, required for the server.

For this we will create a controller with UIWebView (you can use UIWebViewController. We prefer UIViewController with added UIWebView. Delegate - self).

In ViewDidLoad Spotify basic initialization must be carried out:

SPTAuth.defaultInstance().clientID        = SpotifyClientID
SPTAuth.defaultInstance().redirectURL     = NSURL(string: SpotifyRedirectURI)
SPTAuth.defaultInstance().tokenSwapURL    = SpotifyTokenSwapURL
SPTAuth.defaultInstance().tokenRefreshURL = SpotifyTokenRefreshURL
SPTAuth.defaultInstance().requestedScopes = [SPTAuthStreamingScope, SPTAuthPlaylistReadPrivateScope, SPTAuthPlaylistModifyPublicScope, SPTAuthPlaylistModifyPrivateScope]

Where:

  • SpotifyTokenSwapURL and SpotifyTokenRefreshURL we will come back to late.

  • SPTAuthStreamingScope, SPTAuthPlaylistReadPrivateScope, SPTAuthPlaylistModifyPublicScope, SPTAuthPlaylistModifyPrivateScope - access rights for our application

  • SpotifyRedirectURI - our redirectURL, specified in Spotify application settings.

With appearance of controller in viewWillAppear login link must be initialized:

let url = "https://accounts.spotify.com/authorize?client_id=\(SpotifyClientID)&scope=\(SPTAuthStreamingScope)%20\(SPTAuthPlaylistReadPrivateScope)%20\(SPTAuthPlaylistModifyPublicScope)%20\(SPTAuthPlaylistModifyPrivateScope)&redirect_uri=\(SpotifyRedirectURI)&nosignup=true&nolinks=true&show_dialog=true&response_type=code"

let request = NSURLRequest(URL: NSURL(string: url)!)

webView.loadRequest(request)

Parameters:

  • client_id - clientId of our application
  • scope - access allowance
  • redirect_uri - redirect URL to return in application and spot the moment of login completion
  • nosignup = true - to disable autologin
  • show_dialog = true - necessary request/confirmation “Is it you?”
  • response_type = code - type of a returned value (Such variants as code and access token are possible). We are interested in code precisely.

Answer processing is carried out in the following way:

func webView(webView: UIWebView, shouldStartLoadWithRequest request: NSURLRequest, navigationType: UIWebViewNavigationType) -> Bool {

    if request.URLString.hasPrefix(SpotifyRedirectURI) {
    // Example of a correct response::
        // projectName://spotify/callback?code=AQCY0mycN16svdc7Edj3jH1BUw...
        if let fragment = request.URL!.query,
            code = parameterValue(CodeKey, fragment: fragment) {
                // Now transfer URL to Spotify sessions’ constructor
                SPTAuth.defaultInstance().handleAuthCallbackWithTriggeredAuthURL(request.URL, callback: { (error: NSError!, session: SPTSession!) -> Void in
                    if session != nil {
                        // Notification about the login’s success
                        self.dismissViewControllerAnimated(true, completion: nil)
                    } else {
                        // Notification about the login’s mistake
                        self.dismissViewControllerAnimated(true, completion: nil)
                    }
                })
        } else if let fragment = request.URL!.query,
            error = parameterValue(ErrorKey, fragment: fragment) {
            if error == "access_denied" {
        // Cancel
                self.dismissViewControllerAnimated(true, completion: nil)
            } else {
                self.presentViewController(Alert.alertWithText(error, cancelAction: { 
                    self.dismissViewControllerAnimated(true, completion: nil)
                }), animated: true, completion: nil)
            }
        }
    }

    return true
}

private func parameterValue(name: String, fragment: String) -> String? {        
    let pairs = fragment.componentsSeparatedByString("&")
    for pair in pairs {
        let components = pair.componentsSeparatedByString("=")
        if components.first == name {
            return components.last
        }
    }        
    return nil
}

As a result of login implementation the session which is available via SPTAuth.defaultInstance().session is created. The speciality of Spotify session is its duration. Now it is 1 hour. On the expiration of this period you should call the following method: SPTAuth.defaultInstance().renewSession. Everything is alright - just add control/test/verification before every spotify SDK method call SPTAuth.defaultInstance().session.isValid() but there is a BUT:

Created session is stored on the device locally.

3. Tokens’ storage and updating

If you want user to login on a different device without reconnection to their Spotify, you will have to resort to the following tricks:

  • Remember two links - SpotifyTokenSwapURL and SpotifyTokenRefreshURL? These are the links for the requests on your server, which realize the logic of Spotify tokens updating. When you have to update the session, renewSession method call should be a bit corrected:
    SPTAuth.defaultInstance().tokenSwapURL    = SpotifyTokenSwapURL
    SPTAuth.defaultInstance().tokenRefreshURL = SpotifyTokenRefreshURL
    SPTAuth.defaultInstance().renewSession(SPTAuth.defaultInstance().session, callback: { (error: NSError!, session: SPTSession!) -> Void in
        if session != nil {
            // Success 
        } else {
            failure(error: error)
        }
    })

We highly recommend to reassign swap and refresh links before session update. The reason for this is a possible renewSession call in places, when SPTAuth.defaultInstance() is not configured yet.

  • Realize swap and refresh methods on our server. Example of realization on Ruby.

  • Testing. After an hour of work, call renewSession method from SDK, which in its turn addresses your server, gets required information, forms a session and stores it on a device.

P.S. This session can be transformed into a line and back with the help of the following methods:

let sessionData = NSKeyedArchiver.archivedDataWithRootObject(session)
return sessionData.base64EncodedStringWithOptions(.Encoding64CharacterLineLength)

if let restoredSessionData = NSData.init(base64EncodedString: fixedSpotifySession, options: .IgnoreUnknownCharacters),
restoredSession = NSKeyedUnarchiver.unarchiveObjectWithData(restoredSessionData) as? SPTSession {
    SPTAuth.defaultInstance().session = restoredSession
}

Transformed session can be stored locally or on the server, being cipheredbeforehand.

4. Songs stream

Streaming is carried out not from a habitual to us AVPlayer, but from a SPTAudioStreamingController. We saved a Spotify player object as a singleton in Utils class, which makes access to it available from every place:

static let spotifyPlayer = SPTAudioStreamingController(clientId: SpotifyClientID)

While setting up the application (or at the moment when we are sure that we have a connected spotify account) we call its initialization:

Utils.spotifyPlayer.loginWithSession(SPTAuth.defaultInstance().session) { (error: NSError!) in
    if error != nil {
//       if error.code == 9 {
////         self.presentViewController(Alert.alertWithText("Buy spotify premium"), animated: true, completion: nil)
//       } else {
            print(error.localizedDescription)
        }
    } else {
        Utils.isSpotifyPremium = true
    }
}

In the provided example a spotify premium check block (code mistakes check: 9 - a basic account, which does not allow stream) is commented for the reason of not stable request (as SDK in beta is quite a possible scenario). That is why streaming availability flag appears only when there are no mistakes, irrespective of their type.

Player’s main methods

  • Track listing:

    let trackURI = NSURL(string: "spotify:track:\(trackId!)")!
    Utils.spotifyPlayer.playURIs([trackURI], withOptions: SPTPlayOptions(), callback: { (error: NSError!) in
        if error != nil {
            print(error.localizedDescription)
            return
        }                
    })
  • Start/Pause

    Utils.spotifyPlayer.setIsPlaying(false) { (error: NSError!) in
        if error == nil {
            // Pause
        } else {
            print("Failed to stop spotify player: " + error.localizedDescription)
        }
    }
  • Stop:

    Utils.spotifyPlayer.stop(nil)
  • Current position:

    Float(Utils.spotifyPlayer.currentPlaybackPosition)
  • General duration:

    Float(Utils.spotifyPlayer.currentTrackDuration)
  • Seek to time:

    Utils.spotifyPlayer.seekToOffset(Double(newPosition), callback: nil)

Thank you for your time. Hope you will find something useful for yourself!

P.S. Do not forget to logout from Spotify

SPTAuth.defaultInstance().session = nil

Useful links

  1. Spotify iOS SDK
  2. Spotify iOS SDK - tutorial
  3. Spotify Web API
  4. An example of the refresh and swap methods' implementation on Ruby
  5. Spotify Web API Endpoint Reference
  6. Spotify Web API Code Examples & Libraries
SIMILAR POSTS
Dec 06 2016
The article contains a brief description of the latest functionality available after the release of iOS 10.
Nov 23 2016
All necessary features that you should know before developing your own music app. Statistics, estimated cost and our hands-on experience.
Nov 17 2016
Getting started with Kotlin and third-party libraries Glide, Dagger 2, Retrofit 2, Realm, MVP and RxJava on Android.