Spotify AppRemote is not reconnecting once disconnected in iOS - ios

I am using Spotify in my iOS application in order to list and play a playlists. Initially I connect appRemote and fetch essential playlist contents with the following scopes such as .appRemote, .playlist, . playlistReadPrivate etc.
App is working fine but if I receive a phone call, then my appRemote was getting disconnected with the below log:
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.}
I tried disconnecting appRemote in app WillResignActive state and also reenabling(Reconnecting with appRemote.connect()) in foreground state. None of them helps. Is there any way to fix this issue or we have to reauthorize once again. Any help will be much appreciated. Thanks.

I've tried in:
- (void)appRemote:(SPTAppRemote *)appRemote didFailConnectionAttemptWithError:(NSError *)error
to handle reconnect attempts with a counter variable and increasing delay
[self performSelector:#selector(connect2Spotify) withObject:self afterDelay:3.0];
not perfect...but works in most cases..
-(void)connect2Spotify
{
if (self.appRemote!=nil)
{
if (!self.appRemote.connected){
self.appRemote.connectionParameters.accessToken = self.token;
[self.appRemote connect];
}
}
}

Related

Unable to get ReplayKit (w/RPBroadcastActivityViewController) to stream to YouTube live - get "The user declined application recording" error

I'm trying to use ReplayKit to live stream from within an iOS app on iOS 11 and Swift 4. My code succesfully live streams to MobCrush, but when I select YouTube and the broadcast is supposed to get kicked off it fails.
Relevant code:
func broadcastActivityViewController(_ broadcastActivityViewController: RPBroadcastActivityViewController,
didFinishWith broadcastController: RPBroadcastController?,
error: Error?) {
//1
guard error == nil else {
print("Broadcast Activity Controller is not available.")
print("ERROR BROADCASTING: " + error!.localizedDescription)
return
}
//2
broadcastActivityViewController.dismiss(animated: true) {
//3
broadcastController?.startBroadcast { error in
//4
//TODO: Broadcast might take a few seconds to load up. I recommend that you add an activity indicator or something similar to show the user that it is loading.
//5
if error == nil {
print("Broadcast started successfully!")
self.broadcastStarted()
}
}
}
}
It Prints:
Broadcast Activity Controller is not available.
ERROR BROADCASTING: The user declined application recording
Trying to figure out if this is an issue with YouTube or with some permissions/implementation problem on my side.
It's worth noting that ReplayKit streaming clearly does not work for some of the advertised platforms (e.g. Periscope), but I have successfully gotten YouTube ReplayKit to work with some other apps I tested, so it should be possible.
I'm seeing a similar thing.
MobCrush - Works beautifully
Periscope - Stream starts, connects & the entry shows-up in Periscope but the video is blank/inaccessable when you want to view it either live or saved.
Youtube - An error occurs, stopping streaming from starting, yet a Scheduled Livestream entry appears for the live stream you attempted to do. This is scheduled about 8 hours in the past for me. (But I'm sure this value depends on your system clock relative to the US West Coast)
So. It appears only MobCrush seems to have upheld its end of the bargain.

Chromecast Sleep/Background issue in iOS app

I am facing a very big issue while using Chromecast in my application. I am using normal GCKUICastButton to connect to the Chromecast. The video plays well.
I am using the default Cast receiver for my application. When the device goes to sleep, sometimes, Chromecast stops and sometimes, after sleep mode, the device disconnects after some time. After going through lot of forums and Stack Overflow questions, I implemented the below code
extension GCKSessionManager {
static func ignoreAppBackgroundModeChange() {
let oldMethod = class_getInstanceMethod(GCKSessionManager.self, #selector(GCKSessionManager.suspendSession(with:)))
let newMethod = class_getInstanceMethod(GCKSessionManager.self, #selector(GCKSessionManager.suspendSessionIgnoringAppBackgrounded(with:)))
method_exchangeImplementations(oldMethod, newMethod)
}
func suspendSessionIgnoringAppBackgrounded(with reason: GCKConnectionSuspendReason) -> Bool {
guard reason != .appBackgrounded else { return false }
return suspendSession(with:reason)
}
}
Then in my code I wrote the below line
GCKSessionManager.ignoreAppBackgroundModeChange()
Now suddenly, the Chromecast does not disconnect however after few minutes of sleep, it disconnects as well as, kill the app as well. How can I retain the Chromecast play session even if the device goes to sleep or goes to background.
As I am using GCKUICastButton so I am not using the GCKDeviceManager so I am unable to use the ignoreAppStateNotification using GCKDeviceManager, can you advice if I can use that as well.
I have also added the GCKCastOption code as well in AppDelegate.

Why does Social.localUser.Authenticate lead to crash when there is no internet connection in Unity app?

With an internet connection
Everything works flawlessly. There is no memory problem leading to crash.
With no internet connection
The app proceeds to the menu screen, where it eventually crashes because it is out of memory.
I have concluded that the problem lies in the following line of code
Social.localUser.Authenticate
When I comment out the above line, the memory problem goes away when there is no internet connection.
Here is my relevant code
void Start ()
{
Social.localUser.Authenticate(ProcessAuthentication);
}
public void ProcessAuthentication(bool success)
{
if(success)
Debug.Log ("Authenticated");
else
Debug.Log ("Failed to authenticate");
}
Leading up to the crash
2016-02-27 15:46:37.131 BrickBall[449:60670] Received memory warning.
WARNING -> applicationDidReceiveMemoryWarning()
2016-02-27 15:46:37.302 BrickBall[449:60670] Received memory warning.
WARNING -> applicationDidReceiveMemoryWarning()
2016-02-27 15:46:37.349 BrickBall[449:60670] Received memory warning.
WARNING -> applicationDidReceiveMemoryWarning()
2016-02-27 15:46:37.437 BrickBall[449:60670] Received memory warning.
WARNING -> applicationDidReceiveMemoryWarning()
Message from debugger: Terminated due to memory issue
Why would that line of code be causing the out of memory crash when there is no internet connect?
My guess is that you'll eventually need to talk to Unity. Game center will use cached credentials when there's no network connectivity to report that it successfully connected to the server and authenticated, even though it didn't. I have a bug open--and an ongoing discussion--with Apple on this. This behavior allows some game types to continue even when there's no network, then sync up later when connection is restored. However, I ran into problems where I assumed I could do things because GC said it was authenticated, but I really couldn't because it really wasn't. :/
This means apps have to handle three cases:
successful authentication with GC
failed authentication with GC
failed authentication, but reported as successful based on cached data
It's possible that Unity doesn't handle the third situation. To confirm or refute this, try the following:
Confirm that Unity does cleanly handle authentication failures
establish connectivity
log out of game center
Break connectivity (airplane mode, etc)
Retry your app
I would expect that success would be false at this point and run cleanly.
If that works as expected, I'd talk to Unity about how they handle Game Center reporting a (cached) success in a disconnected situation.
Edit2:
I had to go back and look at my code to see exactly how I hardened against it. The scenario was: while completely disconnected and/or in airplane mode, Game Center was presenting the "welcome back" message and localPlayer.authenticated was set to YES... BUT, the error code was set and complaining that it couldn't connect.
I opened bug 22232706, "[GKLocalPlayer localPlayer].authenticated always returns true after any authentication handler is set," and which still has an ongoing discussion. Apple confirmed the behavior, but says its intended.
Below is how I hardened my authentication handler to deal with this situation. It won't help you since Unity is handling this for you, but I thought other readers may find this helpful. (The TL;DR version is: always always always check the error code first, before you check .authenticated or before you check if viewController is set)
[localPlayer setAuthenticateHandler:^(UIViewController *loginViewController, NSError *error)
{
//Note: this handler fires once when you call setAuthenticated, and again when the user completes the login screen (if necessary)
//did we get an error? Could be the result of either the initial call, or the result of the login attempt
//Very important: ALWAYS check `error` before checking for a viewController or .authenticated.
if (error)
{
//Here's a fun fact... even if you're in airplane mode and can't communicate to the server,
//when this call back fires with an error code, localPlayer.authenticated is sometimes set to YES despite the total failure. ><
//combos seen so far:
//error.code == -1009 -> authenticated = YES
//error.code == 2 -> authenticated = NO
//error.code ==3 -> authenticated = YES
if ([GKLocalPlayer localPlayer].authenticated == YES)
{
NSLog(#"error.code = %ld but localPlayer.authenticated = %d", (long)error.code, [GKLocalPlayer localPlayer].authenticated);
}
//Do stuff here to disable network play, disable buttons, warn users, etc.
return;
}
//if we received a loginViewContoller, then the user needs to log in.
if (loginViewController)
{
//the user isn't logged in, so show the login screen.
[rootVC2 presentViewController:loginViewController animated:NO completion:^
{
//was the login successful?
if ([GKLocalPlayer localPlayer].authenticated)
{
//enable network play, or refresh matches or whatever you need to do...
}
}];
}
//if there was not loginViewController and no error, then the user is alreay logged in
else
{
//the user is already logged in
//refresh matches, leaderboards, whatever you need to do...
}
}];

Catching the Parse kPFErrorConnectionFailed error-code & Cancelling the pullToRefresh’s UIActivityIndicatorView in the IOS PFQueryTableViewController

I have two questions as follows:
I will like to catch the kPFErrorConnectionFailed error-code from the query in the IOS PFQueryTableViewController’s queryForTable. How do I go about it?
After the last attempt to connect to Network and I receive [Error]: Network connection failed, How do I cancel the pullToRefresh’s UIActivityIndicatorView which currently continues to load indefinitely?
What I’ve tried:
Concerning catching error-code kPFErrorConnectionFailed, I tried the following (which does not catch the error):
- (void)objectsDidLoad:(NSError *)error {
[super objectsDidLoad:error];
if(error.code == kPFErrorConnectionFailed)
{…}
}
A work around for Catching kPFErrorConnectionFailed is to use Apple's Reachability Class to check if the Parse Network Server is reachable before trying to load data.
A fix to the indefinite pullToRefresh UIActivityIndicatorView is to use kPFCachePolicyCacheThenNetwork as opposed to the kPFCachePolicyNetworkOnly. The kPFCachePolicyNetworkOnly keeps trying to load data from the network even with a bad connection. However, kPFCachePolicyCacheThenNetwork relies on the cached data when the network server is unreachable. Check here for more information

Why won't disconnect work?

I'm writing an application which is based on Apple's Temperature Sensor Application for iOS devices. I'm trying to implement a Disconnect button which will disconnect the currently connected device from the iPhone, however when the disconnect button is pressed there is a BAD_ACCESS error, I know this is memory based but I'm completely at a loss on how to fix it. Code follows.
- (IBAction)clickbutton:(id)sender
{
[[LEConnect sharedInstance] startScan:AccelerometerServiceUUID];
}
- (IBAction)disconnectButton:(id)sender
{
CBPeripheral *peripheral;
if(CBPeripheralStateDisconnected)
{
[[LEConnect sharedInstance] disconnectPeripheral:peripheral];
}
}
The startScan button works correctly but the disconnect button does not. The code in the button is based on the code for finding devices shown below:
if (CBPeripheralStateConnected)
{
[[LEConnect sharedInstance] connectPeripheral:peripheral];
[currentlyConnectedDevice setText:[peripheral name]];
}
earlier in this function the same CBPeripheral *peripheral; pointer is made.
Sorry if this is a dumb question or has been asked before, I'm just really struggling and in desperate need of help! Thanks
The disconnectButton method has two errors. First the peripheral variable is used without being initialized (are you ignoring compiler warnings?). Second, the if statement checks if the peripheral is disconnected and then disconnects it again (you should be checking that the peripheral is connected).

Resources