Game Center - turn based game issue - ios

I'm developing a turn based game for iOS with a custom interface and i discovered a very odd problem with my matchmaking interface. The following code is used to display a list of active matches, i authenticate the user, then get the list of matches and the last step is to load the match so i can display all the info.
The problem appears when i build the app, go to the matchmaking view controller and leave it alone for 5 minutes; then when i try deleting a match i get an error in loadingMatchWithID:
Error Domain=NSCocoaErrorDomain Code=4097 "The operation couldn’t be completed. (Cocoa error 4097.)
The code works fine every time, deleting, creating matches, refreshing, but if i leave the view controller alone for 5 minutes and then try to delete i get the error. The odd part is the that the localPlayer passes an authentication test, and loads correctly the matches array then stops in loading the match.
One other thing happens, if the error appears and i push the home button and open back the app, everything words again and the matches are loaded correctly.
I think i a problem with the authentication, but where is the mistake ?
UPDATE: The issue appears in iOS7, but in iOS6 it works !
[localPlayer authenticateWithCompletionHandler:^(NSError *error)
{
if (error)return;
[GKTurnBasedMatch loadMatchesWithCompletionHandler:^(NSArray *matches, NSError *error)
{
for (int i = 0; i < matchesArray.count; i++)
{
[GKTurnBasedMatch loadMatchWithID:[[matchesArray objectAtIndex:i]matchID] withCompletionHandler:^(GKTurnBasedMatch *updatedMatch, NSError *error)
{
if (error != nil)
{
NSLog(#"Error: %#",error.description);
}
}];
}
}];
}];

I found what was the issue after many days of searching and testing everything. The problem was with quitting and then removing a match, the removeWithCompletionHandler: was inside the participantQuitInTurnWithOutcome: and somehow these actions would log out the player from game center without any notice, and the odd thing was that all the .isAuthenticated test would succeed.
Probably the most frustrating part was that the code worked for the most part, and worked every time on devices below iOS 7.

Related

How to properly test game center achievements

I have implemented GameKit with achievements and leaderboards in my game.
I've tested both and they seem to work.
But in order to test them correctly from the beginning (I did some tests by trials and errors) is there any way to start again completely erasing both?
I tried deleting the app form the GameCenter app of the simulation/phone but when I login again and iOS register the app in GameCenter everything re-appears.
Furthermore I implemented one achievement that can be achieved more than ones. This achievement gives 50 points. Actually I can achieve it more than ones in the game in fact I get the pop-up each time. However in the achievement list I can only see 50 points and not more, possible?
Perhaps, I didn't get the meaning of achievable more than ones..
EDIT:
I'm trying to solve this with the following method
func resetAchievements() {
// Clear all progress saved on Game Center
GKAchievement.resetAchievementsWithCompletionHandler() {(error) in
self.lastError = error
}
}
But it works only when I install the app in the device and not in the Simulator, why?
Perhaps because I don't understand the Apple's Guide
class func resetAchievementsWithCompletionHandler(_ completionHandler: ((NSError!) -> Void)!)
The following will reset all the achievements your local player has earned. You cannot earn an achievement more than once, what you are doing is posting the final value over and over again which is showing you the completion alert. The earned more than once option allows you to accept challenges from friends on that achievement. I recommend reading the introduction guide again as both of these topics are discussed in detail.
[GKAchievement resetAchievementsWithCompletionHandler: ^(NSError *error)
{
if(error == NULL)
{
NSLog(#"Achievements have been reset");
}
else
{
NSLog(#"There was an error in resetting the achievements: %#", [error localizedDescription]);
}
}];

How to catch NSError instance thrown in iOS simulator

I am developing an iOS application with Rubymotion which worked pretty fine until i added a new UIViewController. I have added the controller via xcode and have got just a UITextField as an element in it. On running the simulator and on displaying this particular scene, I get an instance of NSError. I also dont see the UITextField element in my screen.
Can any one please explain as to what it is and how do i handle the NSError and make sense out of it?
Any help at this point would be of great help.
UPDATE: I am getting the NSError instance every time my app is launched first, no matter what the controller is. I had upgraded to Xcode 5.1 yesterday. Wonder if that has something to do with the error.
UPDATE 2: This was result of a confusion on my part. I had assumed the NSError to have been raised after upgrading to Xcode 5.1 and thought it was some bug. The reason being was that I had had started using push notifications at the same time as I had upgraded to 5.1. As push notifications don't work in simulators, the NSError was being returned by the simulator. Messed it up big time and spent quite a few hours trying to debug this problem.
NSError is a class and it is used as the preferred way to handle errors in objective-c. Usually it works like this:
You declare a NSError pointer and sends the address to that one in as a parameter to a method that might fail. If something goes wrong in the method a NSError object is created and populated with info about the error and when the methods returns you can check the error object to se if anything went wrong.
NSError *error;
[someObject someFunctionWithParam:paramOne andError:&error];
if ( error ) {
// Here you can inspect the error and act accordingly
NSLog(#"%#", [error localizedDescription]);
}
If you are the one implementing a method it usually looks something like this.
- (void)someFunctionWithParameter:(NSString *)argOne andError:(NSError **)error {
// something goes wrong
*error = [NSError errorWithDomain:#"SomeDomain" code:500 userInfo:#{#"infoKey": #"some info"}];
}
So about the title of your question. There is no catching NSError's since they are not thrown. Only exceptions are thrown.

Game Kit loadMatchDataWithCompletionHandler never loads match data

I'm building a turn based game kit application and I'm saving and retrieving data using:
saveCurrentTurnWithMatchData:jsonData completionHandler:^(NSError *error)
loadMatchDataWithCompletionHandler:^(NSData *matchData, NSError *error)
I Get all the matches using this:
[GKTurnBasedMatch loadMatchesWithCompletionHandler:^(NSArray *matches, NSError *error) {
and save each of the matches to an array and call the laodMatchData method when I need it. The problem is that the completion handler never returns anything. I guess it's stuck gettings the data and never gets back to me. It loads sometimes but more often than not, it just keeps loading.
Am I missing something?
The problem was with iOS7 beta 1. The problem is solved now.

iOS game center -- frequent CONNECTION INTERRUPTED messages

I am working on an iOS game center game, using GKTurnBasedMatch. Every time an incomplete turn, there is a message in the console like this:
2013-04-26 19:26:45.115 AppName[6439:5a9f] CONNECTION INTERRUPTED
Interestingly, this does not happen when I send a complete turn with
[match endTurnWithNextParticipants: nextParticipants turnTimeout:100000 matchData: data completionHandler:^(NSError* error){
// some block here
}];
but it does happen when I send an incomplete turn with
[match saveCurrentTurnWithMatchData:data completionHandler:^(NSError* error) {
// some block here
}];
Someone else reported a similar problem here: Spurious Game Center player disconnect messages. However, it is difficult to see how the only answer there applies to my situation, as I am creating my matches with GKMatchmakerViewController.
i am having the exact same issue with saveCurrentTurnWithMatchData
infact sometimes i have seen the GameData is not updated with saveCurrentTurnWithMatchData while it returns no error

game center sandbox -- neither truly logged in nor out

I am working on a new version of my app. I was using the sandbox normally for a while, but now all of my devices are stuck with a very strange problem. They appear to be halfway logged into game center. It doesn't work for them, but they can't log out either. Here is my authentication method:
- (void)authenticateLocalPlayer {
GKLocalPlayer* localPlayer = WJLocalPlayer;
WJLog(#"Authenticating local user...");
if (localPlayer.authenticated == NO) {
localPlayer.authenticateHandler = ^ (UIViewController* vc, NSError *error) {
if (error) {
WJLog(#"Authentication failed! %#", [error localizedDescription]);
}
else {
WJLog(#"Authentication succeeded!");
NSString* name = [GKLocalPlayer localPlayer].displayName;
WJLog(#"display name is %#", name);
NSString* alias = [GKLocalPlayer localPlayer].alias;
WJLog(#"alias is %#", alias);
GKTurnBasedEventHandler *ev = [GKTurnBasedEventHandler sharedTurnBasedEventHandler];
ev.delegate = self;
}
};
}
}
And here is what I am seeing from the log statements [WJLog is just my own version of NSLog without the garbage]:
Authenticating local user...
Authentication succeeded!
display name is Me
alias is (null)
I can log in or out in the game center app. It makes no difference. I always see the above. I even tried restoring one of the devices to factory settings. The result was still the same. I also tried disabling and re-enabling game center for the new version of my app. Still the same result.
Any ideas?
You're completely ignoring the UIViewController parameter. You're supposed to present this to the user if it exists, so they can log in. Probably you are only now experiencing this because you logged in to the non-sandbox game center, and now when you run the app it wants to ask you for your sandbox credentials, but instead you're assuming you're authenticated.
You have some other problems here too:
You should set the authenticateHandler once only, soon after your app launches.
You should check localPlayer.authenticated inside your authenticateHandler, and nowhere else, as this is the only place it's guaranteed to be valid. Specifically, it's a meaningless value after you resume from the background and until your authenticateHandler gets called again. If you need it elsewhere, use a global variable that gets initialised to false at startup and also in your applicationWillEnterForeground method, and only gets set to true inside your authenticateHandler when you've determined that the localPlayer is actually authenticated.
Check the error and log it by all means, but it doesn't tell you anything about whether authentication actually succeeded, so remove the 'else'.
Have a look at the documentation here.

Resources