removeWithCompletionHandler causes logging out of Game Center - ios

I am using GKTurnBasedMatch's removeWithCompletionHandler to programmatically remove old (finished, i.e., with status = GKTurnBasedMatchStatusEnded) turn-based matches from Game Center when needed (to avoid extra loading burden, I want to keep the amount of finished matches to a minimum).
I am getting no error and the match is correctly removed.
However, most times I do this, my user gets kicked out of Game Center, so the local player is no longer authenticated. This happens on iOS7, with both iPhone and iPad.
Did anyone experience this? Is there any way around it?
NB: I might as well not remove matches from GC, as I use internal business logic to determine which matches should be listed to the user and show, say, only the 10 (unfinished) most recent ones. However, I am afraid that hundreds of games might be kept in GC and that this might slow down interactions with GC when I have to list ongoing matches.

If you're calling removeWithCompletionHandler inside of a completion handler for another Game Center API call then you might be experiencing the very same issue that's been plaguing me for a while. The solution I tried today involves simply delaying the call to removeWithCompletionHandler for a few seconds, like so:
[match performSelector:#selector(removeWithCompletionHandler:) withObject:^(NSError *error) { /* callback code here */ } afterDelay:3.0];
For me, this worked great but meant I had to rework a few things in relation to displaying active games... so be wary of that if this solution works for you. Also, it's worth noting that the 3 second delay is an arbitrary value I picked and it worked for me.
In my case, I was calling removeWithCompletionHandler inside the completion handler block for the various quit methods on GKTurnBasedMatch.
I imagine there is some issue on Game Center's end where the two requests cannot be made so close together without resulting in such woe. This is evidenced by the fact that the issue did not ever occur for me when I slowly stepped through the calls to Game Center.
EDIT
Here's some cool news; seems like they've fixed the bug in iOS 8. I built an app which demonstrates the bug for bug-reporting purposes (originally for iOS 7). I've tested that same app running in iOS 8 and it seems that the bug has been squashed.
Finally.
IMO, you should consider adding the removeWithCompletionHandler call in now.

Related

Where to put code that gets checked frequently?

I have some code that needs to get called frequently, such as check what day it is, if it's the next day then move the day strings in the tableView.
Now I thought that the viewDidLoad would get called all the time and so it would be 'fine' to put it in there. However, I've left the simulator overnight, and I've pressed the home button and clicked again, changed VCs etc. and viewDidLoad hasn't been hit.
What are my options for doing sporadic checks such as, is it a new day? As x happened etc.
In this specific case, you can subscribe to NSCalendarDayChangedNotification to be notified when the date changes and respond accordingly in your view controller. In general, didBecomeActive or viewDidAppear would likely work.
What are my options for doing sporadic checks such as, is it a new day
It depends what the meaning of "is" is! In particular, "is" when? You say "sporadic", but that's just fluff. When do you need to know this? To what stimulus do you want to respond? When the user opens your app? Then put it in applicationDidBecomeActive. Every day at noon? Then run an NSTimer. Really, the problem here is that you don't seem to know, yourself, just when you need to perform these checks.
Whilst in your app, its quite easy to continually check for something. You simply create a background thread. However, what you describe is a thread that persists from outside the app's lifecycle.
Have a read on this documentation provided by Apple itself. You have to have good excuse to put a background thread. The scope of such thread is limited to only certain scenarios such as downloading background stuff, playing sounds etc.
For your scenario, I'd look at applicationDidBecomeActive(_:) found in your Application Delegate. There you can mimic such continual check. Beware however, don't put heavy word load on start up or your app might be killed automatically if it fails to become active in reasonable amount of time.

GameKit Turn-based listener is not reliably called when matchData changes

I am using Game Center turn-based matches for my card game. It feels like a good fit because people want to check their email or write a text sometimes between turns. Some users have said that they do really want to be able to have a very asynchronous playing experience.
For the people that are keeping the game open between their turns, I want to update the screen to reflect things that the other players are doing on their turns. I have set up a listener on GKLocalPlayer that responds to player:receivedTurnEventForMatch:didBecomeActive. The documentation says that this will get called when match data is saved by another player even if it doesn't become the player's turn (the player on the current device). That doesn't appear to be true 100% of the time. In fact, it appears that it only gets called about 1 in 3 times that match data is saved by other players. It seems more reliable when it becomes the player's turn, but even that isn't 100% reliable.
I am using saveCurrentTurnWithMatchData:completionHandler: on GKTurnBasedMatch to save match data that doesn't end the current player's turn and I'm calling endTurnWithNextParticipants:turnTimeout:matchData:completionHandler: on GKTurnBasedMatch to save the data when it does end the current player's turn. There are a few scenarios where I want to call saveCurrentTurn… with updated matchData. In my game, you can have computers playing in your multiplayer game as well. So, a human player may play a card and then a computer may play a card before that GKPlayer's turn ends. There are also scenarios where an individual player may play twice. (eg. A player plays the last card on a trick. That player takes the trick and gets to lead the next trick.)
I have set up a ton of logging around this and I can see clear scenarios where one device calls saveCurrentTurn… and the completionHandler is called without an error and the other device doesn't get notified with a call to player:receivedTurnEvent… I have also added logging to verify that each time I'm calling saveCurrentTurn… that I'm calling it with new matchData. I'm not making redundant calls.
If I go to the device that didn't get the updated matchData and force it to load the matchData for the match again, it gets the updated data. So, it's definitely getting saved.
I have tried throttling the calls to saveCurrentTurn… so that they don't happen in immediate succession and that didn't help.
Both devices in my testing are running iOS 8.4. There appears to have been an issue in iOS 8.3 that is fixed now (see this question). This Apple forum post also reports this issue 2 years ago and it appears that bug reports were filed and marked fixed.
Has anyone else seen this? I would love to know that I'm doing something wrong. Any ideas are very welcome.
I do something similar. In my game, each player has multiple pieces, saving the match when each piece moves so that other players--if they're in the game--can watch what's happening in real time. Like you describe, the Game Center messaging is almost completely useless.
As you referenced, in 8.3, the "end of turn" messages were completely broken. As of 8.4, they happen most, but not all, of the time. As you're seeing, the "match has been saved" notifications are also erratic. Here are some tips I've used to increase the success rate:
Slow down the saves. If you save too fast, only the last one arrives at the recipient. I set up an NSArray queue, and each time I want to save the match, I add the new matchData to that queue. I have a timer loop running that does the actual saveCurrentTurnWithMatchData, pops the item off the stack if the save was successful, and then sets up a new timer to call itself again a little later. I'm using 2 second intervals which seems to be working well.
Append each new piece of data, don't overwrite. Put a sequence number on each piece of data. So, if you save seq numbers 1, 2, 3 and 4, but the recipient only receives a notice for #4, the records for 1, 2 and 3 are there in the match object. The recipient needs to track the last record it read, and then iterate through any new records from that point when it receives an updated matchData.
I also use the queue's NSArray writeToFile: function to maintain a list of the pending saves. If the user exits the game before the queue is flushed, I reload the queue NSArray from disk at the next startup
Note that even with this mechanism, the notifications to the recipient are erratic. Generally speaking, they arrive in batches of 4+. Then nothing happens until 3 or 4 more saves happen, which again all show up together. Making 1 save and letting the game sit for 10 minutes will probably never generate a notice on the recipient's machine. But, if you save 4 or 6 times in a row, all of them tend to show up in a burst.
Sometimes, the notifications just stop for a few hours. Not sure if this a sandbox flaw or a game-center-in-general flaw. There are no failures of any kind, the messages just stop working for a while. Sometimes, the next morning, they show up in a burst. Sometimes not. In the end, I've stopped relying on the notifications. I set up another timer loop to continuously download the match. It checks if it has become my turn or not, it checks if new updates have been added to the matchData. Then calls player:receivedTurnEventForMatch:didBecomeActive. As far a receivedTurnEventForMatch: knows, it was launched because of an event and it merrily goes on about its business.
It does seem that saving the match is pretty prompt. If you don't get an error, it seems pretty certain that the updated match is immediately available for other players to consume... they just need to know to consume it. The messaging framework, though, has to be viewed as completely unreliable and non-guaranteed. Hence, the timer loop to continuously poll the match.
Edit: arguably, once I implemented #2, #1 shouldn't really matter. Any notification received by the recipient will trigger reading all new records in the data. But, this "hardening" has evolved over the past few months as I wrestle with Game Center's shortcomings. I just haven't gotten around to removing #1.

iOS touchesMoved delay between first and second calls

I have some problems in my iOS app.
I have checked timestamps in the touchesMoved with clock() function and use difference between current and previous values. Difference between 1st and 2nd events is greater than others.
Have you any ideas?
iOS is not a hard-realtime OS. There is generally no expectation that touch events will be delivered with regular frequency/uniform periods. You should not structure your app to depend on that. Events are delivered via the main thread, which could be delayed by other processing (drawing, etc.)
EDIT: If you're seeing huge differences in periods between the 1st and 2nd touches (relative to the periods between subsequent touch events), the first step would be to run the app in Instruments' Profiler template to see if some work that you're doing on the main thread in response to the first touch is causing the delay. If the delay is of your own doing, then fixing that is the first order of business.
Beyond that, you could try using various signal processing approaches to re-sample your data into uniform periodic data, but the problem is that any of those algorithms won't be able to deliver the first re-quantized point until at LEAST the 2nd (and more likely the 3rd or even later) event comes in, so if the principal problem is "The 2nd event doesn't come as soon as you'd like", then this won't help you.
Another good test: Try making an empty single-view sample app that does nothing but log the events as they come in -- if you see the same "odd" delay between 1st and 2nd touches in that trivial sample app, then that's just "how it is," and you can stop wasting time trying to change the OS behavior and move on to some different approach/solution.

ios game gets out of sync while analysing process in Instruments - how can I avoid that?

I am building an iOS game and I notice that the game performs fine while running normally in the XCode debugger. However, when i run it from within Instruments (Product-> Profile to trace Leaks), the game freezes when Instruments displays 'Analyzing Process' in the left sidebar. After that the game messes up all its state since some parts of the game that were being analyzed froze up while other parts kept going.
Is this something I can/need to fix or is it sufficient to make sure the game runs in release?
If a fix is needed, what do I need to do to make it work?
Update 1:
So we found the issue - the same problem repros even if we are playing the game, press the home button and click on the game icon and continue playing.
The issue is that most of our work is done in the update method, and it relies on the value of the (ccTime)dt parameter. The value of dt is usually < 0.1 seconds, and occasionally somewhere upto 0.5 seconds).When we pause (either by clicking the home button, or when instruments pauses the game to take a snapshot) and resume playing, the value of dt is several seconds! And that throws all our calculations out of range.
We tried a temporary (but ugly) workaround that fixes the issue: at the beginning of the update method, we add this:
if(dt > 1)
return;
And it now works as expected - doesn't go out of sync. However, this is not a permanent solution, since sometimes, the values of dt are legitimately close to 1 second, and in resource crunched situations, this may lead to stutter (or worse).
We also considered another (equally ugly) solution of storing the previous value of dt, and then check in the update method:
if(dt > 10 * prevDt)
{
return;
}
We tried calling unscheduleUpdate in AppDelegate.m's applicationDidEnterBackground, and called scheduleUpdate in the applicationWillEnterForeground method, but that approach did not work.
What is the best way to deal with updates with erratic time values due to external pauses?
Thanks
Anand
I don't know much about how cocos2D works, but if you've run out of options, I would simply clamp the delta time to an upper range. In whatever update methods used delta time, I would do the following.
double delta = (dt > 0.5) : (0.5) ? (dt);
Use delta instead of dt from that point onward. The result is that any frame with a delta of over half a second will be limited to a delta of half a second. You don't want to skip frames, because then you could potentially skip many frames in a row which would be bad for the user; they'd probably think the app crashed or something. However, you don't want to run a frame with a large delta because then delta-dependent objects and forces will get multiplied many times for that frame, preventing you from catching collisions and stuff. So, we prevent the game from getting completely screwed over while not skipping frames by simply clamping the value downwards.
The negative is that the app will appear to move more slowly when your frame rate drops to 2 frames per second. You lose the benefit of a time-based update method, which is the ability to always make the game appear to run at a well defined speed, when the engine is overburdened.
To be honest, if you ever run at 2 frames per second for an extended period of time, you've got bigger problems to worry about. I don't think the user will notice if objects move slightly more slowly per second if the game is only rendering once every who-knows-when anyways.
Unfortunately this is a problem, for which there is no sure answer; at least not without access to your system to run a whole variety of checks.
The failure in the profile may be because your game is running tight loops the timing of which get's upset in unpredictable ways and your game is crashing due to timing or resource issues (where those timing issues don't crop up with the debugger in the same way). If that's the case, there is probably not much you can do about it. Or it may be because there is a problem in your code. The problem is it can be very difficult to figure out which of these is the case. It's probably best to assume the problem is in your code though and do some further investigation.
The first thing to do, if you haven't done it already, is run the static analysis tool (Analyse from the Product menu in Xcode). Consider each of the raised errors carefully, and work to remove all of them. Sometimes they might seem obvious and you think you can ignore them, but some prodding reveals they are a symptom of a deeper problem.
If you haven't tried already, try running the instrument to check for zombies. There's a high chance this will fail also if the allocation instrument is failing, but if there are some stale references to de-allocated objects hanging around they could be causing the problem you are experiencing. Another instrument you can try is the performance analyser, to check where your app is spending most of it's time. There may be some really significant problem with the overallocation of resources you are not aware of. If you can't run the memory profiler, it will be difficult for you to see if this is the case, but using the performance analyser, it might be possible to see if your app is getting hung up for too long somewhere it shouldn't be.
Lastly - if all else fails and this may be a sledgehammer to crack a nut - and also may not in any case provide the solution. If you aren't using ARC, consider how long it would take to convert your app to using it (definitely create a branch first before doing it though). The Apple algorithms for object allocation/deallocation are very efficient and there is a very good chance if you have subtle memory management errors, they will be eliminated by Automatic Reference Counting.

Adding pin on mapView is not working on appstore version

I have submitted my app and been accepted couple of weeks ago. However, I have an issue that one of my critical functionality which is adding pin is not working but it works in building version. Adding pin requires URL connection. Could you please look at the answer that I get from apple developer and guide me please?
It shouldn't be causing your problem (at least not directly), but in general
having networking code like this live inside your UI code is very problematic
for a number of reasons:
-On general architecture grounds, it's a large violation of the MVC (model
view controller) architecture. The pin annotation is obviously heavily tied
to your UI (the view), while networking is clearly part of your backend (the
model).
-Because iOS REQUIRES the main thread to remain responsive, attaching code
that does IO or computation to the main thread is inherently risky. It can be
done and work well, but doing it incorrectly will inevitably lead to crashes.
NSURLConnection works in background and interactions with UI should go on main thread.
So, if you add your pin at the connectionDidFinish method for example, you should do like this:
[mapView performSelectorOnMainThread:#selector(addAnnotation:) withObject:yourAnnotation waitUntilDone:[[NSThread currentThread] isMainThread]];
Hope that helps you. I wrote from windows but the line should work.
I'm not exactly sure about what you are trying to achieve. Are App folks complaining because you are fetching URL directly in the Main thread ? Could you just send a code snippet showing how you fetch urls ?
One option would be to drop the pin (not tied to URL connection at all) and once the pin drops, load your data for the callout or annotation (which is what I'm assuming you are doing). You can always put a loading indicator or progressHUD up to tell the user they need to wait for something. It isn't exactly ideal, but in instances where you need to wait for data, what are you going to do?
Apple really doesn't care about blocking the UI so long as the user is aware you are blocking it. Like a progress bar or progress HUD. The user can see the app is still working and doing something. Not showing anything and having the UI just freeze while it waits for the main thread/main loop to return isn't going to work. It is just bad design and Apple will likely ding you for it.

Resources