receivedTurnEventForMatch giving stale data

In my turn-based game, I receive GKListener event receivedTurnEventForMatch and decode the match.matchData. On occasion, the matchData is clearly stale and is from the previous turn. If I call the MatchMaker ViewController up and select that same match, the data is not stale, so it's not a matter of not calling endTurn.

I have tried both loadMatchWithID and loadMatchesWithCompletionHandler after receiving the receivedTurnEventForMatch, but the data is still stale.

Advice?

Answered by DTS Engineer in 884963022

What you're seeing is a propagation timing issue — the push notification that triggers receivedTurnEventForMatch can arrive before the updated match data is fully available on the server. That's why both the GKTurnBasedMatch delivered with the callback and a subsequent loadMatch(withID:) return stale data.

A fixed delay is fragile because the propagation time varies with server load and network conditions. A more robust approach is to retry loadMatch(withID:) with a comparison check — for example, if the match still shows the previous participant as the current player, or the matchData hasn't changed from your last known state, wait briefly and retry. Something like an exponential backoff starting at 1 second, up to a reasonable cap, gives the server time to propagate without relying on a magic number.

If you haven't already, please file a feedback report with Feedback Assistant and include your sample project. The behavior you're describing — where loadMatch(withID:) returns stale data even after the turn event fires — is worth tracking. Please post the feedback number here so we can follow up.

At the moment, my workaround is to call loadMatchWithID after a 2-second delay if the match received by receivedTurnEventForMatch shows that the current participant is not the localPlayer.

Two-second delay seemed to work for awhile - I even saw it trigger the loadMatchWithID because it got stale data. Then, I even got stale data on a loadMatchWithID. Not sure how to proceed with this.

I tried gemini-recommended strategy of calling loadMatchesWithCompletionHandler (all matches) - made no difference. A 5-second delay seems to solve the problem, at least for now.

What you're seeing is a propagation timing issue — the push notification that triggers receivedTurnEventForMatch can arrive before the updated match data is fully available on the server. That's why both the GKTurnBasedMatch delivered with the callback and a subsequent loadMatch(withID:) return stale data.

A fixed delay is fragile because the propagation time varies with server load and network conditions. A more robust approach is to retry loadMatch(withID:) with a comparison check — for example, if the match still shows the previous participant as the current player, or the matchData hasn't changed from your last known state, wait briefly and retry. Something like an exponential backoff starting at 1 second, up to a reasonable cap, gives the server time to propagate without relying on a magic number.

If you haven't already, please file a feedback report with Feedback Assistant and include your sample project. The behavior you're describing — where loadMatch(withID:) returns stale data even after the turn event fires — is worth tracking. Please post the feedback number here so we can follow up.

The problem with including a sample project is that it doesn't happen all the time. It just randomly happens at any point in a game, so it's hard to imagine that it can be easily duplicated without playing the game for awhile. I'm surprised that the push notification is sent out before the data is available - that seems like a pretty huge bug IMHO.

Intermittent bugs are still worth filing — a Feedback Report doesn't need to reliably reproduce. It needs to give engineering enough information to identify the failure when it happens and to correlate against the server-side logs.

A few things that make this kind of report useful:

  • A sample project that uses the same receivedTurnEventForMatchloadMatch(withID:) path. If you can extract that flow out of your game into a small standalone app that two test accounts can play against each other, that's ideal. If extracting is impractical, your actual app build can work too — engineering may follow up with what would help most.
  • Logging that captures, for each receivedTurnEventForMatch: the timestamp, the match ID, the local player ID, the reported current participant, and the matchData state at that moment. Then for each retry: the timestamp, what was returned, and whether it matched the previous state.
  • The match IDs and approximate timestamps from past occurrences you've observed. Engineering can cross-reference these against server logs for the specific matches.
  • A sysdiagnose collected as soon as practical after a stale-data occurrence — the logs cover enough time that even hours later is useful.

The push-arriving-before-data observation is the part worth highlighting in the Feedback Report — that's the behavior engineering will want to see characterized.

In the meantime, the exponential-backoff-with-comparison approach is still the right shipping fix, since it doesn't require waiting for a fix in any particular release. Confirming the data actually changed (current participant flipped, or matchData differs from your last known state) before treating it as new gets you predictable behavior today.

Please post the Feedback number here when you have it, so we can follow up.

receivedTurnEventForMatch giving stale data
 
 
Q