spotify / ios-sdk

Spotify SDK for iOS
https://developer.spotify.com/documentation/ios/
653 stars 186 forks source link

Spotify App disconnected on new song playing. #22

Open Sapta14 opened 6 years ago

Sapta14 commented 6 years ago

Session initiated successfully with SPTConfiguration where playURI is mentioned as blank string, with the function sessionManager.initiateSession(with: scope, options: .default) after that Spotify app started playing last played track. Problem happens when we are trying to play another song through appRemote.playerAPI!.play(uri, callback: nil) player failed to play new song each time rather in debug we are getting some logs like

AppRemote: Sending request: <SPTAppRemoteCallRequest: 0x129ea3a10; id: 2, method: com.spotify.play_uri>

AppRemote: Disconnected with error: Error Domain=com.spotify.app-remote.transport Code=-2001 "End of stream." UserInfo={NSLocalizedRecoverySuggestion=Reconnect to the Spotify app., NSLocalizedDescription=End of stream., NSLocalizedFailureReason=One of the streams has reached the end.}

and after that delegate function appRemote(_ appRemote: SPTAppRemote, didDisconnectWithError error: Error?) is invoked.

Any idea? Thanks in advance

jackfreeman commented 6 years ago

@Sapta14 can you provide some more information so we can try and debug?

Spotify App Version: iOS Device & Version: Xcode Version:

1) Does the NowPlaying sample app work for you?

2) I assume you are setting the returned token on the appRemote's connection params like this?

3) Do other commands work?

4) What's the URI you are trying to play? Have you tried albums or playlists too? Where are you getting the URI from?

Sapta14 commented 5 years ago

@jackfreeman here are the informations you asked for Spotify App Version: 8.4.71 (latest as per AppStore) iOS Device & Version: iPhone 6, 11.0.3 Xcode Version: 9.4.1

  1. Does the NowPlaying sample app work for you? sample app working, started playing song on Spotify app while initiating.

  2. I assume you are setting the returned token on the appRemote's connection params like this? Yes, here is my code

    func sessionManager(manager: SPTSessionManager, didInitiate session: SPTSession) {
        appRemoteObj.connectionParameters.accessToken = session.accessToken
        appRemoteObj.connect()
    }
  3. Do other commands work? Since app disconnected, appRemote.playerAPI returns nil. So unable to check other functionalities.

  4. What's the URI you are trying to play? Have you tried albums or playlists too? Where are you getting the URI from? URI :: spotify:track:3PURovRKyJaBmgzPfmMJ4O This track belongs to the playlist "Texas Music Now" (featured playlists) Fetching the playlists from web API "https://api.spotify.com/v1/browse/featured-playlists"

One more scenario I wants to share with you that, if I use this API appRemoteObj.authorizeAndPlayURI(uri) song started playing each time.

Thanks in advance.

jackfreeman commented 5 years ago

@Sapta14 Do you get the App Remote delegate callback for a successful connection after calling connect()?

func appRemoteDidEstablishConnection(_ appRemote: SPTAppRemote)

Since it looks like you are using the SPTSessionManager for auth and attempting to use App Remote, are you also setting playURI on SPTConfiguration to either an empty string or a specific URI? See here

For App Remote to connect the Spotify app must be alive in the background. This requires the Spotify app to play music. authorizeAndPlayURI will trigger playback as it assumes that by using this API you are trying to connect App Remote. SPTSessionManager can be used with and without App Remote so if you intend to use it with App Remote you must set playURI on SPTConfiguration to initiate playback after auth. You also have to request the app-remote-control scope.

If your intention is to auth the user, pull info from the Web API, and then start playback I recommend the following approach:

1) Auth the user using SPTSessionManager with app-remote-control scope (and any other scopes you need) 2) Option 1 - Using SPTSessionManager - Once you have the uri to play set it on SPTConfiguration and call initiateSession again. This will start playback in Spotify and allow you to connect with App Remote. After App Remote is connected you can then use the playerAPI.play(uri) function for playing other items. Option 2- Using authorizeAndPlayURI - Once you have the uri to play call appRemoteObj.authorizeAndPlayURI(uri). This will trigger playback in the Spotify app and allow you to connect App Remote. You can ignore the token returned from this auth process. After App Remote is connected you can then use the playerAPI.play(uri) function for playing other items.

The main thing is that music needs to be playing the first time you call connect() on App Remote.

Sapta14 commented 5 years ago

@jackfreeman Here are the steps I following

  1. First I called to get the session sessionManagerObj.initiateSession(with: scope, options: .default) To get the session manager Object need a configuration object and it looks like
    fileprivate lazy var configuration: SPTConfiguration = {
        let configuration = SPTConfiguration(clientID: clinet_id, redirectURL: URL(string: redirect_url)!)
        configuration.playURI = ""
        configuration.tokenSwapURL = URL(string: token_swap_url)
        configuration.tokenRefreshURL = URL(string: token_refresh_url)
        return configuration
    }()

    On initiate this delegate is called

    func sessionManager(manager: SPTSessionManager, didInitiate session: SPTSession) {
        appRemoteObj.connectionParameters.accessToken = session.accessToken
        appRemoteObj.connect()
    }

    On successfull Remote connection

    func appRemoteDidEstablishConnection(_ appRemote: SPTAppRemote) {
        appRemoteObj.playerAPI?.delegate = self
        appRemoteObj.playerAPI?.subscribe(toPlayerState: { (result, error) in
            if let error = error {
                  //error in subscription
            }
        })
    }

    Then Spotify app starts playing the last track. To get the session access token for fetching playlists needs to call initiateSession method and to get session object, configuration object is required and as per documentation playURI needs to pass a valid string else player will not initialise. At this point if I try to play another song that issue is happening that I mentioned first.

Here are the scopes I am requesting [.appRemoteControl, .playlistReadPrivate, .streaming, .userReadPlaybackState, .userModifyPlaybackState, .userReadCurrentlyPlaying, .userReadRecentlyPlayed, .userReadPrivate, .userTopRead, .userReadPrivate, .userReadEmail, .userLibraryRead]

jackfreeman commented 5 years ago

@Sapta14 since you are trying to play a track are you doing so on a premium account?

Also does the call to playerAPI?.subscribe succeed?

Sapta14 commented 5 years ago

@jackfreeman after successful remote connection establishment by calling func appRemoteDidEstablishConnection(_ appRemote: SPTAppRemote) I am checking the user is premium user or not by calling

SPTUser.requestCurrentUser(withAccessToken: "access_token") { [weak self] (error, user) in
       if let localUser = user as? SPTUser, localUser.product == .premium {
               debugPrint("Premium")
       } else {
               debugPrint("Not Premium")
       }
}

and it is returns the user as a premium user. While called

appRemoteObj.playerAPI?.subscribe(toPlayerState: { (result, error) in
})

on success this delegate is called func playerStateDidChange(_ playerState: SPTAppRemotePlayerState)

jackfreeman commented 5 years ago

Can you try a few of these steps to attempt to play a URI: 1) Only request appRemoteControl scope 2) Don't subscribe to player state

Lastly, can you possibly provide a sample application in which you can reproduce this behavior so I can take a closer look at what might be occurring?

*As a side not you should use SPTAppRemoteuserCapabilities from SPTAppRemoteUserAPI's fetchCapabilitiesWithCallback instead of the old streaming SDK to determine if a user canPlayOnDemand (is premium). Additionally, you do not need the .streaming scope to use App Remote.

Sapta14 commented 5 years ago

@jackfreeman ok I will try with your suggested steps, once completed let you know the outcomes.

jackfreeman commented 5 years ago

@Sapta14 any update on this?

bregman90 commented 5 years ago

I experienced a similar issue:

1) sessionDidInitiate successful (requestedScope: appRemoteControl) 2) appRemoteDidEstablishConnection successful (don't subscribe playerAPI to playerState) 3) then, executing appRemote.playerAPI?.play with a spotify track ID triggers didDisconnectWithError (Code=-2001 "End of stream.")

Happy to share any more app info.