iMessaged-based invitations for GameCenter for iOS 10

500 views Asked by At

I'm trying to update my app to work correctly with the new features of GameCenter in iOS10.

I create a new GKGameSession on device1, get a share URL, and all that works fine. I send the share URL out via a share sheet to device 2.

Device2 clicks the link, the device briefly displays 'Retrieving...' and then launches my app. Great! But, now what? Is there context information available for this URL that I can somehow access? Otherwise I have no way how to respond when the app is launched.

Previously you'd get a callback to something adhering to the GKLocalPlayerListener protocol, to the method player:didAcceptInvite:, and you could join the match that way. But with these iCloud-based messages, the player might not be even logged into GameCenter, right? This part seems to have been glossed over in the WWDC presentation.

Also, as of today (12/28/2016) there is no Apple documentation on these new methods.

2

There are 2 answers

0
TheBasicMind On BEST ANSWER

Since the GKGameSessionEventListener callback session:didAddPlayer: only fires if the game is already running, to be sure you can process this callback every time requires a work around. I've tested this and it works.

When you send out an iMessage or email invite to the game, don't include the Game Session Invite URL directly in the message. Instead use a registered URL that will open your app when opened on a device on which your app is installed. Check here to see how:

Complete Tutorial on iOS Custom URL Schemes

But add a percent escaped encoding of the game invite URL as a parameter to this URL thusly (I'm assuming the registration of a url e.g. newGameRequest but it will be best to make this quite unique, or even better - though it requires more setup, try Universal Link Support as this will allow you to direct users who don't have your app installed to a webpage with a download link)

let openOverWordForPlayerChallenge = "newGameRequest://?token="

gameState.gameSession?.getShareURL { (url, error) in
    guard error == nil else { return }
    // No opponent so we need to issue an invite
    let encodedChallengeURL = url!.absoluteString.addingPercentEncoding(withAllowedCharacters:.urlHostAllowed)
    let nestedURLString = openOverWordForPlayerChallenge + encodedChallengeURL!
    let nestedURL = URL(string: nestedURLString)!
}

send the URL in a message or email or WhatsApp or whatever. Then in your app delegate, add the following:

func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool {
    var success = false
    if let queryString = url.query {
        if let urlStringToken = queryString.removingPercentEncoding {
            let token = "token="
            let startIndex = urlStringToken.startIndex
            let stringRange = startIndex..<urlStringToken.index(startIndex, offsetBy: token.characters.count)
            let urlString = urlStringToken.replacingOccurrences(of: token, with: "", options: .literal, range: stringRange)
            if let url = URL(string: urlString) {
                if UIApplication.shared.canOpenURL(url) {
                    UIApplication.shared.open(url, options: [:], completionHandler: nil)
                    success = true
                }
            }
        }
    }
    return success
}

Now you can be sure the session:didAddPlayer: will be called. What's the betting this workarround is good for about 2 weeks, and they fix this in the next release of iOS showcased at WWDC 2017 ! Update: this problem hasn't been fixed - so the workaround above remains good!

1
Thunk On

I agree, the lack of documentation is frustrating. From what I can see, we have to:

  • add <GKGameSessionEventListener> protocol in the class' header
  • Then session:didAddPlayer: fires on the joining player's device after accepting an invite link.

update: Unfortunately, I'm not surprised to hear your results. I hadn't tried all of those scenarios, but GKTurnBasedMatch had similar shortcomings. The way I got around it there was: I added a list of player statuses to match data (invited, active, quit, etc). I gave the player a view of "pending invitations." When they opened that view, I would load all of their matches and display the entries where the player was in invited state. With GKGameSession, that should work too.

Or, it might be easier if you could maintain a local list of sessions that you are aware of. Whenever the game becomes active, pull the entire list of sessions from the server and look for a new entry. The new entry would have to be the match the player just accepted by clicking the share URL.