Hash88
Hash88

Reputation: 712

GameKit presenting authentication view controller automatically instead of waiting for me to present it

According to Apple's documentation, you should set GKLocalPlayer.local.authenticateHandler, once at app launch, with a closure that accepts a viewController and an error. And if the viewController is not nil, you should save it and present it to the user at the appropriate time.

I copy/pasted their example to my own app, and the behavior I see is completely different.

Instead of giving me a view controller to present later, immediately after setting the closure, the standard Game Center UI is presented. At this point my closure is not yet called. Then, after either logging in or tapping "Cancel", the Game Center UI is dismissed. At this point the closure is called. error correctly reflects whether the user logged in. But in either case viewController is always nil.

If I restart the app after tapping "Cancel", the system seems to remember that I cancelled. Then the UI is not presented, and the closure is called with viewController still being nil and error being non-nil.

This behavior is undesirable to me for a couple reasons:

  1. I would like to set this closure on app launch, but I don't want the UI to present right away (like Apple suggests in its docs).
  2. If a user declined to log in and then wants to do something that requires logging in, I would like to present the saved view controller in-app, rather than showing an alert telling them to go to Settings (which is the most I can do at the moment, AFAIK).

For context, I am setting the closure in a top-level SwiftUI view like this:

struct ActiveGamesView: View {
  @State private var isPresentingAuth = false
  @State private var gameCenterAuthVC: UIViewController?
  @State private var gameCenterEnabled = false
  @State private var isMultiplayerGamingRestricted = true
  @State private var isPersonalizedCommunicationRestricted = true

  var body: some View {
    NavigationView {
      VStack {
        Spacer()
        NavigationLink(destination: Text("This is the online play area")) {
          Text("Play Online")
        }
      }
    }
    .onAppear() {
      authenticateGameCenter()
    }
    .fullScreenCover(isPresented: $isPresentingAuth) {
      GameCenterAuthView(underlyingViewController: gameCenterAuthVC!)
      // This is a UIViewControllerRepresentable that just presents gameCenterAuthVC.
    }
  }

  private func authenticateGameCenter() {
    gameCenterAuthVC = viewController
    isPresentingAuth = viewController != nil
    gameCenterEnabled = error == nil
    isMultiplayerGamingRestricted = GKLocalPlayer.local.isMultiplayerGamingRestricted
    isPersonalizedCommunicationRestricted = GKLocalPlayer.local.isPersonalizedCommunicationRestricted
  }
}

As expected given the current unexpected behavior, my fullScreenCover closure never gets called, so my saved gameCenterAuthVC never gets presented.

My question is: is this auto-presenting UI normal, and Apple just didn't update their docs? Or am I doing something wrong?

Upvotes: 5

Views: 636

Answers (1)

Fault
Fault

Reputation: 1339

yes i've also thought that the apple docs are confusing on this point ("Present the view controller so the player can sign in.") i don't think you need to (or can?) "capture" the viewController and save it for later. so when/if you want to log in to Game Center you call the following

GKLocalPlayer.local.authenticateHandler = { viewController, error in
}

this need not happen at app launch. and the authenticateHandler closure can be empty.

Upvotes: 2

Related Questions