3

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?

Fattie
  • 27,874
  • 70
  • 431
  • 719
JessThePest
  • 155
  • 2
  • 11

1 Answers1

1

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

  1. establish connectivity

  2. log out of game center

  3. Break connectivity (airplane mode, etc)

  4. 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...
     }

 }];
Thunk
  • 4,099
  • 7
  • 28
  • 47
  • pretty interesting post, guy! – Fattie Feb 28 '16 at 15:12
  • I am sure it is being reported as successful because even when I don't have internet connection and open the game, Game Center opens (the pop up banner at the top with "Welcome Name" appears). With the amount of games made though Unity I don't know why it is so hard to find documentation on this problem. I'm sure adding Game Centre is really common :S – JessThePest Feb 28 '16 at 17:18
  • 1
    @Joe: thanks, it was an extremely painful exercise. It took me a few days to finally narrow down the pattern: once logged in, you always report as authenticated, even when subsequent logins don't connect. The overwhelming majority of authentication handler examples on the internet are vulnerable to this situation. – Thunk Feb 28 '16 at 18:04
  • 1
    @Jess: That sounds exactly like the scenario I ran into. And you are dead on re: documentation. Apple's GC docs are all marked "preliminary" (for several years now), vague, missing many key details, and (in some cases) flat out wrong. :( – Thunk Feb 28 '16 at 18:07
  • Thanks for your information. I guess now I know why it is happening, but not really how to justify it in my code :S – JessThePest Feb 29 '16 at 20:23