Updated answer:
My original idea, below, turns out to not be reliable. You cannot count on the saveCurrentTurnWithMatchData to send timely notifications. It works most of the time, but at least 10% of the time, it fails to send a notifications as well. Of all the ideas posted here, the only thing I have found that works 100% reliably is to set up a timer loop on the non-active machines and continuously watch until you become active.
-(void)isMatchActive:(NSTimer *)timer
{
NSString *matchID = (NSString *)timer.userInfo;
[GKTurnBasedMatch loadMatchWithID:matchID withCompletionHandler:^(GKTurnBasedMatch *match, NSError *error)
{
GKLocalPlayer *localPlayer = [GKLocalPlayer localPlayer];
GKTurnBasedParticipant *currentParticipant = match.currentParticipant;
if ([localPlayer.playerID isEqualToString:currentParticipant.player.playerID])
{
//we have become active. Call the event handler like it's supposed to be called
[self player:localPlayer receivedTurnEventForMatch:match didBecomeActive:false];
}
else
{
//we are still waiting to become active. Check back soon
float dTime = 5.0;
gameController.IOS8BugTimer = [NSTimer scheduledTimerWithTimeInterval:dTime
target:self
selector:@selector(isMatchActive:)
userInfo:matchID
repeats:NO];
}
}];
}
Original Answer:
So, I have a work around that is kludgy but looks promising. Note that, per my comment above, the subsequent players still receive events after the current player executes saveCurrentTurnWithMatchData. So I use that send my own custom signal:
I added a string to my matchData for the "next player's ID."
Right before I call endTurnWithNextParticipants, I set that string to the ID of the next player in the rotation.
I call saveCurrentTurnWithMatchData
I moved the call to endTurnWithNextParticipants inside saveCurrentTurnWithMatchData's completion handler, to ensure it doesn't fire until after saveCurrentTurnWithMatchData.
For the purposes of this work around, I added the GKTurnBasedEventHandlerDelegate to my code. handleTurnEventForMatch is broken in 8.3 too, but I consolidated all of my workaround code there and didn't have to make changes to receivedTurnEventForMatch
In handleTurnEventForMatch, I check to see if the "next player" string is me. If so, I assume that the current player just saved the game in step 2, signalling his intention to pass the turn to me.
I start a timer loop, reloading the match data until it shows that I have become the active player.
When I see that I am now the active player, I reset my custom next player string to nil and I manually call receivedTurnEventForMatch passing it the match data I just downloaded.
To summarize, player1 sends an extra saveTurn event to signal that he intends to end the turn. When player2 receives that signal, he re-reads the match data until it shows that he has become active. Then he calls his own receivedTurnEventForMatch with the updated match data, allowing the turn to proceed.
I haven't gotten through all my scenarios yet, but it looks like this is going to work.