I am using custom view to show the list of matches in my turn based game. With the custom view I am having issues showing the list of current games the player is involved when the device is offline. But when I check the game center default view the matches show fine even when offline. The code I am using to populate my array is as follows (extracted from the book by Ray Wenderlich)
[GKTurnBasedMatch loadMatchesWithCompletionHandler:^(NSArray *matches, NSError *error)
{
if (error)
{
NSLog(#"%#", error.localizedDescription);
}
else
{
NSMutableArray *myMatches = [NSMutableArray array];
NSMutableArray *otherMatches = [NSMutableArray array];
NSMutableArray *endedMatches = [NSMutableArray array];
for (GKTurnBasedMatch *m in matches)
{
GKTurnBasedMatchOutcome myOutcome;
for (GKTurnBasedParticipant *par in m.participants)
{
if ([par.playerID isEqualToString: [GKLocalPlayer localPlayer].playerID])
{
myOutcome = par.matchOutcome;
}
}
if (m.status != GKTurnBasedMatchStatusEnded && myOutcome != GKTurnBasedMatchOutcomeQuit)
{
if ([m.currentParticipant.playerID
isEqualToString:[GKLocalPlayer localPlayer].playerID])
{
[myMatches addObject:m];
}
else
{
[otherMatches addObject:m];
}
}
else
{
[endedMatches addObject:m];
}
}
// 6
allMyMatches = [[NSArray alloc]initWithObjects:myMatches,otherMatches,endedMatches, nil];
NSLog(#"%#",allMyMatches);
[self.tableView reloadData];
}
}];
Any ideas why this is happening?
loadMatchesWithCompletionHandler: will talk to the Game Center servers and I would expect it to fail if your device is offline. You are checking for error to be not nil. Is error.localizedDescription telling you you're not connected?
My bet would be that the Game Center default view will show you matches that it cached from the last time you were connected. You could do this too, but remember that you would also have to cache the matchData. Not sure how important that would be since you won't be able to submit your turn anyways.
Related
I have a issue with the Login and sharing dialogs on my app. About a week ago they started appearing only on Portrait mode (either normal or upside down), but the landscape modes does not work.
NSArray *permissions = #[#"user_photos",
#"user_likes",
#"user_friends",
#"email"];
_loginManager = [[FBSDKLoginManager alloc] init];
_loginManager.loginBehavior = FBSDKLoginBehaviorWeb;
[_loginManager logInWithReadPermissions:permissions fromViewController:self handler:^(FBSDKLoginManagerLoginResult *result, NSError *error) {
if(result.isCancelled || error)
{
[self pressedCancel];
return;
}
NSSet* declinedPermissions = result.declinedPermissions;
for (NSString *declinedPermission in declinedPermissions) {
if(!nullOrEmpty(declinedPermission))
{
[self pressedCancel];
return;
}
}
if ([FBSDKAccessToken currentAccessToken]) {
// self.settings.facebookToken = [FBSDKAccessToken currentAccessToken].tokenString;
// Send data capture
if([EventData sharedInstance].dataCaptureSettings.enabled)
{
[FacebookHelper getUserInfoWithCompletion:
^(NSDictionary* userInfo, NSError* error)
{
if(!nullOrEmpty(userInfo))
{
[self captureData:userInfo];
} else {
[self captureData:nil];
}
[self didSignIn];
}];
return;
} else {
[self captureData:nil];
[self didSignIn];
}
}
}];
Here's a MPOC to replicate the issue:
https://www.dropbox.com/s/p62vajqfk916bz1/FBTest.zip?dl=0
Any ideas what may be causing this? Or how could I make it work on Landscape mode as well?
Thanks!
just in case anyone else has this issue, it was fixed on the newest Facebook SDK for iOS (4.38.1)
I'm trying to make a leaderboard in my game with Game Center. I post the high score like so:
GKScore *myScoreValue = [[[GKScore alloc] initWithCategory:#"grp.high_scores"] autorelease];
myScoreValue.value = self.game.scoreMeter.score;
NSLog(#"Attemping to submit score: %#", myScoreValue);
[myScoreValue reportScoreWithCompletionHandler:^(NSError *error){
if(error != nil){
NSLog(#"Score Submission Failed");
} else {
NSLog(#"Score Submitted");
id appDelegate = [[UIApplication sharedApplication] delegate];
[appDelegate displayLeaderBoard:nil];
}
}];
I see "Score Submitted" as expecting, and it brings up the Game Center leaderboard view, but it just reads "No Scores"
I know other people have said you need at least two accounts, but I've tried with three already.
For each account, the game shows up for them in the Game Center App, and when I query for the top ten scores:
- (void) retrieveTopTenScores
{
GKLeaderboard *leaderboardRequest = [[GKLeaderboard alloc] init];
if (leaderboardRequest != nil)
{
leaderboardRequest.playerScope = GKLeaderboardPlayerScopeGlobal;
leaderboardRequest.timeScope = GKLeaderboardTimeScopeAllTime;
leaderboardRequest.range = NSMakeRange(1,10);
[leaderboardRequest loadScoresWithCompletionHandler: ^(NSArray *scores, NSError *error) {
if (error != nil)
{
NSLog(#"Error grabbing top ten: %#", error);
}
if (scores != nil)
{
NSLog(#"Top ten scores: %#", scores);
}
}];
}
}
each user only sees their own score.
So why is the leaderboard empty, and why is each user only seeing their own score?
Sandbox can be messy sometimes, but let me ask you this. Is your self.game.scoreMeter.score an int?
If so, try doing this:
myScoreValue.value = [[NSNumber numberWithInt:self.game.scoreMeter.score] longLongValue];
for setting the value of the GKScore object. Let me know if it changes anything.
Well, it turns out you need to be signed in to game center with a developer sandbox account for it to work correctly
I have an issue where I see memory usage climbing (but no obvious leaks in Instruments) in my app.
I have a test project with two viewControllers: MainViewController and PhotoViewController. The MainViewController contains a single button that simply pushes PhotoViewController via a uinavigationcontroller pushViewController method.
In PhotoViewController, I am using the ALAssetsLibrary to populate a UITableView with images. I essentially do this in two parts. First, I check to see what assetGroups are available, as I need to show images from the Camera Roll and the Photolibrary. Once that is done, I call another method to enumerate through the actual Assets.
Here is the strange behavior: if I push the PhotoViewController and let it finish the entire enumeration and populate the UITableView, and then pop out back to the MainViewController, everything is fine.
However, if I repeatedly and rapidly push and pop out of the PhotoViewCOntroller (while it hasn't yet finished enumerating and populating the UITableiew),then I see my memory usage gradually climbing until the app finally dies. I don't see any obvious leaks in Instruments.
I don't know the relevant code, but here are two methods that use to enumerate. Of course, in dealloc, I am releasing the relevant ivars.
Is there some way to cancel an enumeration upon pop?
Just as a note, I am basing my test code off this project (https://github.com/elc/ELCImagePickerController), although heavily customized. However, I just tested with that code and the same issue happens. Note that you would only see memory usage climb if you have sufficient ALAssets to enumerate. If there are too few, then it would finish enumerating beforeyou couldpop back out.
Thank you!
- (void)getAssetGroups
{
// Load Albums into assetGroups
dispatch_async(dispatch_get_main_queue(), ^
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// Group enumerator Block
void (^assetGroupEnumerator)(ALAssetsGroup *, BOOL *) = ^(ALAssetsGroup *group, BOOL *stop)
{
if (group == nil)
{
// check what data is available
if([savedPhotosGroup numberOfAssets] > 0 && [libraryGroup numberOfAssets] > 0)
{
// User has both Camera Roll and Photo Library albums
self.tableData = [NSMutableDictionary dictionaryWithObjectsAndKeys:
savedPhotoAssets, NSLocalizedString(#"PHOTOPICKER_CAMERAROLL", nil),
libraryPhotosAssets, NSLocalizedString(#"PHOTOPICKER_PHOTOLIBRARY", nil),
nil];
self.sectionKeys = [NSArray arrayWithObjects:NSLocalizedString(#"PHOTOPICKER_CAMERAROLL", nil), NSLocalizedString(#"PHOTOPICKER_PHOTOLIBRARY", nil), nil];
}
else if([libraryGroup numberOfAssets] == 0)
{
// User only has Camera Roll
self.tableData = [NSMutableDictionary dictionaryWithObjectsAndKeys:
savedPhotoAssets, NSLocalizedString(#"PHOTOPICKER_CAMERAROLL", nil),
nil];
self.sectionKeys = [NSArray arrayWithObjects:NSLocalizedString(#"PHOTOPICKER_CAMERAROLL", nil), nil];
}
else
{
//User only has Photo Library
self.tableData = [NSMutableDictionary dictionaryWithObjectsAndKeys:
libraryPhotosAssets, NSLocalizedString(#"PHOTOPICKER_PHOTOLIBRARY", nil),
nil];
self.sectionKeys = [NSArray arrayWithObjects:NSLocalizedString(#"PHOTOPICKER_PHOTOLIBRARY", nil), nil];
}
NSLog(#"Done enumerating groups");
[self performSelectorInBackground:#selector(enumeratePhotos) withObject:nil];
[self.tview performSelector:#selector(reloadData) withObject:nil afterDelay:1];
return;
}
ALAssetsGroupType groupType = [[group valueForProperty:ALAssetsGroupPropertyType] unsignedIntValue];
if(groupType == ALAssetsGroupSavedPhotos)
{
self.savedPhotosGroup = group;
}
else if(groupType == ALAssetsGroupLibrary)
{
self.libraryGroup = group;
}
};
// Group Enumerator Failure Block
void (^assetGroupEnumberatorFailure)(NSError *) = ^(NSError *error) {
NSLog(#"A problem occured %#", [error description]);
};
// Enumerate Albums
[library enumerateGroupsWithTypes: ALAssetsGroupSavedPhotos | ALAssetsGroupLibrary
usingBlock:assetGroupEnumerator
failureBlock:assetGroupEnumberatorFailure];
NSLog(#"Draining pool");
[pool drain];
});
}
-(void)enumeratePhotos {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSLog(#"enumerating photos");
[savedPhotosGroup enumerateAssetsUsingBlock:^(ALAsset *result, NSUInteger index, BOOL *stop)
{
if(result == nil)
{
return;
}
CustomAsset *customAsset = [[[CustomAsset alloc] initWithAsset:result] autorelease];
[customAsset setParent:self];
[savedPhotoAssets addObject:customAsset];
}];
[libraryGroup enumerateAssetsUsingBlock:^(ALAsset *result, NSUInteger index, BOOL *stop)
{
if(result == nil)
{
return;
}
CustomAsset *customAsset = [[[CustomAsset alloc] initWithAsset:result] autorelease];
[customAsset setParent:self];
[libraryPhotosAssets addObject:customAsset];
}];
NSLog(#"done enumerating photos");
[tview performSelectorOnMainThread:#selector(reloadData) withObject:nil waitUntilDone:NO];
// only do this if I want to re-select some assets
if(assetsToRestore)
{
for(NSDictionary *dict in assetsToRestore)
{
NSIndexPath *indexPathToRestore = [dict objectForKey:#"selectedAssetIndexPath"];
int tagToRestore = [[dict objectForKey:#"selectedAssetTag"] intValue];
[self selectAssetWithIndexPath:indexPathToRestore andIndex:tagToRestore];
}
}
[pool drain]; }
correct me if i'm wrong, I thought using autorelease pools was supposed to be done like this now:
#autoreleasepool {
(statements)
}
That worked for me.
I'm making a turn based iPhone game with a maximum of 2 players, in my game You can get a score which will be put in a Game center leaderboard. The next time you start the game up your highest score will be retrieved and you can make it even higher because the app counts on from that previous highscore. I found some code to get the authenticated players score But I also wanna show the score of the opponent you are playing and I was wondering how?
This is the code I found for the authenticated player (yourself)
if([GKLocalPlayer localPlayer].authenticated) {
NSArray *arr = [[NSArray alloc] initWithObjects:[GKLocalPlayer localPlayer].playerID, nil];
GKLeaderboard *board = [[GKLeaderboard alloc] initWithPlayerIDs:arr];
if(board != nil) {
board.timeScope = GKLeaderboardTimeScopeAllTime;
board.range = NSMakeRange(1, 1);
board.category = #"MY_LEADERBOARD";
[board loadScoresWithCompletionHandler: ^(NSArray *scores, NSError *error) {
if (error != nil) {
// handle the error.
NSLog(#"Error retrieving score.", nil);
}
if (scores != nil) {
NSLog(#"My Score: %i", ((GKScore*)[scores objectAtIndex:0]).value);
}
}];
}
[board release];
[arr release];
}
is there a way to modify this so it will show what I want?
Also I have a problem showing NSLog(#"My Score: %i", ((GKScore*)[scores objectAtIndex:0]).value);
in a UILabel :S
Hope someone can help me out
NSLog(#"My Score: %lld", ((GKScore*)[scores objectAtIndex:0]).value);
the score values are 64bit integers, not int's
intValue
longValue
longLongValue <<==
I've been doing quite a bit of work on a fun little iPhone app. At one point, I get a bunch of player objects from my Persistant store, and then display them on the screen. I also have the options of adding new player objects (their just custom UIButtons) and removing selected players.
However, I believe I'm running into some memory management issues, in that somehow the app is not saving which "players" are being displayed.
Example: I have 4 players shown, I select them all and then delete them all. They all disappear. But if I exit and then reopen the application, they all are there again. As though they had never left. So somewhere in my code, they are not "really" getting removed.
MagicApp201AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
context = [appDelegate managedObjectContext];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *desc = [NSEntityDescription entityForName:#"Player" inManagedObjectContext:context];
[request setEntity:desc];
NSError *error;
NSMutableArray *objects = [[[context executeFetchRequest:request error:&error] mutableCopy] autorelease];
if (objects == nil) {
NSLog(#"Shit man, there was an error taking out the single player object when the view did load. ", error);
}
int j = 0;
while (j < [objects count]) {
if ([[[objects objectAtIndex:j] valueForKey:#"currentMultiPlayer"] boolValue] == NO) {
[objects removeObjectAtIndex:j];
j--;
}
else {
j++;
}
}
[self setPlayers:objects]; //This is a must, it NEEDS to work Objects are all the players playing
So in this snippit (in the viewdidLoad method), I grab the players out of the persistant store, and then remove the objects I don't want (those whose boolValue is NO), and the rest are kept. This works, I'm pretty sure. I think the issue is where I remove the players. Here is that code:
NSLog(#"Remove players");
/**
For each selected player:
Unselect them (remove them from SelectedPlayers)
Remove the button from the view
Remove the button object from the array
Remove the player from Players
*/
NSLog(#"Debugging Removal: %d", [selectedPlayers count]);
for (int i=0; i < [selectedPlayers count]; i++) {
NSManagedObject *rPlayer = [selectedPlayers objectAtIndex:i];
[rPlayer setValue:[NSNumber numberWithBool:NO] forKey:#"currentMultiPlayer"];
int index = [players indexOfObjectIdenticalTo:rPlayer]; //this is the index we need
for (int j = (index + 1); j < [players count]; j++) {
UIButton *tempButton = [playerButtons objectAtIndex:j];
tempButton.tag--;
}
NSError *error;
if ([context hasChanges] && ![context save:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
UIButton *aButton = [playerButtons objectAtIndex:index];
[players removeObjectAtIndex:index];
[aButton removeFromSuperview];
[playerButtons removeObjectAtIndex:index];
}
[selectedPlayers removeAllObjects];
NSError *error;
if ([context hasChanges] && ![context save:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
NSLog(#"About to refresh YES");
[self refreshAllPlayers:YES];
The big part in the second code snippet is I set them to NO for currentMultiPlayer. NO NO NO NO NO, they should NOT come back when the view does load, NEVER ever ever. Not until I say so. No other relevant part of the code sets that to YES. Which makes me think... perhaps they aren't being saved. Perhaps that doesn't save, perhaps those objects aren't being managed anymore, and so they don't get saved in. Is there a lifetime (metaphorically) of NSManaged object? The Players array is the same I set in the "viewDidLoad" method, and SelectedPlayers holds players that are selected, references to NSManagedObjects. Does it have something to do with Removing them from the array? I'm so confused, some insight would be greatly appreciated!!
It looks like you're removing your player objects from your various internal arrays, but not deleting them from the NSManagedObjectContext itself.
After a lot more debugging (Quite a bit) I found the issue. The code worked fine, until you "Removed" the first player, or the player in slot 0. I looked at when the players are filtered:
while (j < [objects count]) {
if ([[[objects objectAtIndex:j] valueForKey:#"currentMultiPlayer"] boolValue] == NO) {
[objects removeObjectAtIndex:j];
j--;
}
else {
j++;
}
}
And realized that if the first player is not playing, then j becomes -1, and then it breaks out of the loop and fails to validate the rest of the players, causing the weird bug to have them all back in the game. It had nothing to do with memory management at all, but I did fix some leaks in my effort to find the bug. Thank you!