How to reliably determine which match got created in response to GKTurnBasedMatchmakerViewController success - iphone

On iOS 10 I am trying to create a match using the GKTurnBasedMatchmakerViewController. I can bring up the view so the user can choose matchmaking or an invite. I can't figure out how to reliably determine which match got created in response to that. The problem is that, in iOS 10, the didFindMatch method of the GKTurnBasedMatchmakerViewControllerDelegate has been deprecated. That method used to be called with the match. The old days were easy!
The deprecation message says "use GKTurnBasedEventListener player:receivedTurnEventForMatch:didBecomeActive". The docs and stack overflow threads are filled with reasons why that method gets called. See this thread for a good list: Gamecenter ios 9 GameCenter GKLocalPlayerListener methods not called. If you imagine a user that already has several games going, you can see that this method will be called for a lot of different reasons and it could happen at any time, as far as I know. My question is: how to determine which of these calls is the "I just created a match for you, here it is!" call.
Some examples I think won't work:
Simply assuming the first call to receivedTurnEventForMatch that happens after you bring up the GKTurnBasedMatchmakerViewController seems wrong since receivedTurnEventForMatch could be notifying the user that it is their turn in another game. Unless the system guarantees that, while the view is up, they will only call this method with the match that corresponds to the view. That seems like a pretty big hack, so I'm assuming they don't do it.
A call to receivedTurnEventForMatch with the other player in "Matching" state and didBecomeActive=true seems to be a solution for the first player in a match, but not the second (since both players are done matching).
Looking at the MatchID and seeing if we've "seen it before", and if not, assuming it is the match that just got created seems unreliable since the user might have uninstalled the app and then reinstalled and we've forgotten all about which matches we've "seen before".
I'm stuck, any help is appreciated.

Double check your status when the event handler fires. I believe you should still be in invited state until you accept the match.
edit: Actually, I believe there will be two conditions that indicate a new match when the event handler fires:
You are in the invited state (you joined)
Everyone else is in the invited or automatch state (you started the match)
edit 2:
Checking my old code, it turns out that I looked at the matchData object. Since I knew that player 2 doesn't receive the invite until player1 ended the turn (and so forth), and since I knew that player1 had to save match data in order to end the turn, if matchData.length was greater than zero, I assumed I was joining a game in progress. I didn't rely on the participant status. But that doesn't solve your problem.
The other thing I did was create my own header struct which I inserted at the start of the NSData. In that header, player1 would set status flags for each other player, that would be obvious when those players joined. I totally ignored the participant status in the match object.
Regarding your third bullet, instead of saving the list locally, you could save it remotely using cloudKit. The cloudkit data will persist across installs/uninstalls unless you specifically delete it.
The gist with cloudkit is:
when you enable it in your app, your app gets a "container" with a public database that all users can share plus a private database unique to each app user.
You can create a record in the private database with a "Bytes" field
You can save a NSMutableArray directly into the aforementioned Bytes field
You can read back a NSMutableArray directly from the Bytes field

Related

observer design pattern in rest aplications

I'm trying to learn design patterns, and I have come with the Observer pattern. I think I understand the concept itself, but I don't see when to use it.
I try to explain myself. I work mostly with web applications, so, stateless applications. Normally, the client makes a petition from the browser (for example, update a record). then the operation is completed.
Let us suppose that I want to notify some persons every time a record is updated. It seems to me the perfect scenario for the Observer patter, but when I think of it, it will end something like:
user makes petition of update.
get all the persons that have to be notified.
put all the persons that have to be notified in the observer.
make the update. (that also will make the notification for the observer pattern).
but... doing it this way, I have to iterate all the persons that I want to notify twice!
And because its a stateless application, I have to go and get all the persons than needs to be notified every time!
I don't know if the observer pattern is more useful for other types of applications, but I can only think of this pattern in a static form, I mean, making the Observer static.
I know I'm losing something, it's a common and accepted pattern, everyone accept it as a valid solution for this concrete problem. What I am not understanding?
First, let's straighten out the terminology.
Each person who wants to be notified is an Observer.
Each type of event which can trigger a notification is an Observable.
Each Observer (person) needs to register itself with the server. It sends a request essentially saying, "I'm interested in foo Observables," which in this case would be, "I'm interested in update events." The server maintains mappings of who is interested in which events.
Every time the server makes an update, it iterates over the mapping of update Observers and sends a notification to each of them.
The advantage is that the server and its Observables have no compile-time knowledge of who the Observers are. Observers are free to register (and unregister) themselves at runtime for any event(s) they are interested in.

Post single notification with varying object types

I have a class that acts as a wrapper around AVPlayer, and one of the functions it serves is to post notifications every 1 and 10 seconds during playback (ie make addPeriodicTimeObserverForInterval: more convenient in the general case).
Previously, the object I was sending with this notification was the player wrapper itself (ie ABPlayer.sharedPlayer). Today I had the need to allow for some objects to only receive notifications about a specific media item's playback. This can be accomplished by sending [[someAVURLAsset URL] absoluteString] as the notification object (when the asset in the AVPlayer is an AVURLAsset, of course).
The prompted the question: is it appropriate for a single notification to, in different situations, post with different types of objects? I understand the value in sending specific objects or sending nil (catch-all), but I don't recall seeing a situation where an alternative type of object could be sent. In my case, though, it seems to make sense.
I could simply send two distinct notifications, but since these are always only ever being sent to notify observers of a single event, and they are always being sent from the same place in code, they simply feel like a single notification.
I realize what I have is possible and working, but I'm curious if there's a compelling reason to avoid this pattern.
As long as the scenarios in which the different object types will be sent to the observers are well understood and documented, there's no technical reason why you can't do it. It may make more contextual sense to post a different notification for each object type. It would certainly help any developers who may end up maintaining your code.

Handle invitation to GKTurnBasedMatch without game center view controllers

I am working on a simple turn-based word game, and I'm having a difficult time figuring out how invitations are working. When user A invites user B to a game, I want user B to be able to see this game in my custom match interface (ie a screen where all of user B's games are listed). Things are fine if user B clicks on the notification when he is invited to a game, but I still want the game to be visible through my own interface if user B just navigates to the app on his own.
Whenever this match interface screen appears, I am using [GKTurnBasedMatch loadMatchesWithCompletionHandler:] to get all of the local player's matches. With this, I would assume that user B would be able to see the match he has been invited to, but this new match doesn't show up. If I accept the invitation through the GKTurnBasedMatchmakerViewController, the user enters the game as expected, but what makes this even more puzzling is that if I open up my GKTurnBasedMatchmakerViewController and then cancel without accepting the invitation, a new match now shows up in my custom match interface, but it lacks the match data that was sent by user A.
To sum up, I am really just wondering how to properly display and accept GKTurnBasedMatch invitations with a custom interface (rather than having to use GKTurnBasedMatchmakerViewController). Thanks!
This question seems to be of interest to a couple people, so here's a rough approximation of what I ended up doing.
First of all, the reason new matches weren't showing up at all was just that I wasn't calling [GKTurnBasedMatch loadMatchesWithCompletionHandler:] every time I wanted the view to refresh like I though I was. So no real issue there as I recall.
The real issue was the case where I was receiving and displaying the new GKTurnBasedMatch, but none of the data (i.e. the opposing player's first move) was available. Essentially, [GKTurnBasedMatch loadMatchesWithCompletionHandler:] doesn't seem to guarantee providing you with the most up-to-date GKTurnBasedMatch objects available. To make sure the matches are up-to-date, I had to also call [match loadMatchDataWithCompletionHandler:], on each match returned by [GKTurnBasedMatch loadMatchesWithCompletionHandler:] (where match is one of those GKTurnBasedMatches). This returns the most current match data associated with that match as an NSData object. I was then able to use this NSData to make sure all the matches in my match table were refreshed to reflect the most recent changes in game center.
In short, use loadMatchDataWithCompletionHandler on your GKTurnBasedMatch objects to make sure their data is up to date.
UPDATE: now includes the actual answer for receiving invitations.
I have struggled with programmatically handling invites for turn-based matches for close to a week. I finally found the answer. I'm gonna super-highlight it because it took me so long to find:
Game Center treats turn-based invitations as turn events. They are not handled like other invitations.
Turn-based events are handled in this function in the GKLocalPlayerListener protocol:
player(_ player: GKPlayer, receivedTurnEventFor match: GKTurnBasedMatch,
didBecomeActive: Bool)
When you recieve the match, check if you're invited to it, and presto. You've received an invitation.
BUT:
Through frustrating trial and error I have found some caveats, which hopefully can save you some serious time:
It just plain doesn't always work. Believe it or not, Game Center is unreliable. This means you need a back-up system that reviews your local player's open matches and searches them for new invitations. That itself pertains to caveat #2.
A player that is invited to a match (for instance say playerJake is invited to matchFoo) will not actually get that invitation nor see that match in the matches returned by loadMatches until it is their turn. Apparently Game Center does not actually involve any player on a match's invitation list in any way until it is their turn.
If you can identify a match you're invited to, but haven't responded to, you must call acceptInvite(...) directly on that match. So if playerJake inspected the matches retrieved by loadMatches, and was able to detect that matchFoo still had an open invitation, playerJake has to call matchFoo.acceptInvite( /* ...completion handler stuff here... */), and then happy playerJake is off and running.
From this you should be able to get your own programmatic matching system to work. Best of luck, and I mean it!

Handling Invitations for Programmatic Turn-Based Game

Thanks to the updates to GameKit API in iOS 6, I am finally able to implement my turn-based board game the way it should be, complete with turn timeouts and better programmatic creation of matches. However, I am running into an issue that I cannot seem to solve. My desire is to have Game Center running entirely invisible to the end-user, so that everything is programmatic and uses my own custom interfaces.
Therefore, I use my own custom table view to display matches, not the default GKTurnBasedMatchmakerViewController. Right now, I have no problem displaying open matches using the -loadMatchesWithCompletionHandler: method. I also use a custom screen to create a match, with a direct creation for auto-match (not a problem) and a table view that loads Game Center friends of the localPlayer for invitation. Since the playersToInvite attribute can now be filled with playerID's, this is possible in iOS 6.
My main problem is handling the invitation on the recipient's side. Lets say I invite Bob to play my game in a two-player match. Right now I can't seem to find a notification for a new invite on Bob's end. The -handleTurnEvent: only gets called for existing matches or if the banner notification is touched (which I can't guarantee the user will do), and -handleInviteFromGameCenter: does nothing for me in this case.
The only way I have come up with to detect new invites and thus update my custom game view controller is to call the -loadMatchesWithCompletionHandler: method and check for new matches in which lastTurnDate of the invited participant is nil and against an existing array of open matches. I run this check about every 10 seconds in the background since I can't find a notification in GKTurnBasedEventHandler that is called when a new invite is received. Please help!
EDIT: In the end, I have just implemented a pull-to-refresh functionality. There is no way without implementing polling or some other method that would just waste the user's data on their phone, so on demand refreshing is the most ideal solution in my opinion.
Please see this : GKInvite Reference and more specifically inviteHandler.
You just need to register an inviteHandler which will be called after Bob accepts the invite in GK/GC.
T.

EKEvent identifier changes when selecting another calendar

I have a meeting planner app that has its own sqlite database and attaches it self to an EKEvent from the calendar framework. I'm using the event identifier as a foreign key for my meeting data. The problem resides when a user changes the event's calendar in the calendar application. The identifier changes and I got no way of knowing that it changed since it was made outside my app.
Please let me know if anyone has any ideas
as of iOS 5.0 there is no way to uniquely identifying an event with certainty
The documentation warns that "If the calendar of an event changes, its identifier most likely changes as well."
Another gotcha is that all instances of a repeating event have the same identifier; -[EKEventStore eventWithIdentifier:] seems to return the first instance. (I've even seen events with a nil identifier; I can't remember what the exact cause is.)
If -eventWithIdentifier: returns a non-repeating event, then you're probably good to go. Otherwise, the easiest way around both of these is probably to search all calendars for events at around the same time and use a heuristic to determine the best match, or ask the user to pick the corresponding event if none of the matches are good enough.
You might also want to check what Google Calendar does when you edit a single instance of a repeating event (it might create an event with a new identifier and add a "hole" in the original event's repeat information, which may break your heuristic).
I'm facing with this problem right now, trying to keep synced EventStore with the internal DB on my app, my solution is to make a mapping between EKEvents and events inside the app, if I found matching it's ok but all the events that don't match with EKEvents will be deleted, and all the EKEvents without match will be added, doing this also the EKEvents that are just being modified will be added during the sync.