I'm creating a public method to return all of my matches from my gamecenterhelper.m
I have this:
+(NSArray *)retrieveMatchesWithMatchData {
__block NSArray *myMatches = nil;
[GKTurnBasedMatch loadMatchesWithCompletionHandler:^(NSArray *matches, NSError *error) {
if (error) {
NSLog(#"There was an error loading matches");
}
else {
myMatches = matches;
}
}];
return myMatches;
}
but this returns null when I call it, even though I have active matches. the call method looks like this:
NSLog(#"My matches: %#",[GameCenterHelper retrieveMatchesWithMatchData]);
Thanks for your time!
Thats the nature of blocks. Your block is executed asynchronously. You cannot load Game Center matches synchronously. Let's make this an instance method:
-(void)retrieveMatchesWithMatchData {
[GKTurnBasedMatch loadMatchesWithCompletionHandler:^(NSArray *matches, NSError *error) {
if (error) {
NSLog(#"There was an error loading matches");
}
else {
[self matchesLoaded:matches];
}
}];
}
Then you process the matches in this method:
-(void)matchesLoaded:(NSArray *)matches {
//do something with your matches
}
EDIT:
There is an easy way to do what you want. You can use Apples standard view controller to show current matches:
GKMatchRequest *request = [[GKMatchRequest alloc] init];
request.minPlayers = minPlayers;
request.maxPlayers = maxPlayers;
GKTurnBasedMatchmakerViewController *mmvc =
[[GKTurnBasedMatchmakerViewController alloc]
initWithMatchRequest:request];
mmvc.turnBasedMatchmakerDelegate = self;
mmvc.showExistingMatches = YES;
[self presentViewController:mmvc animated:NO completion:nil];
Related
I am attempting to download Facebook albums of photos from a user in my app. Unfortunately although I do have an access token, I am getting zero albums from the requests. I am not getting an error, just getting zero. Why? If you would like to see any more code or ask more questions, just ask. Note that I have authorized the current user's Facebook permissions when they signed up, and I've since quit the app and opened it many times (don't think this would be an issue, since I have an access token..?)
- (void)getAlbums:(OLFacebookAlbumRequestHandler)handler {
if ([FBSDKAccessToken currentAccessToken]) {
// connection is open, perform the request
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
NSString *graphPath = #"me/albums?limit=100&fields=id,name,count,cover_photo";
if (self.after) {
graphPath = [graphPath stringByAppendingFormat:#"&after=%#", self.after];
}
FBSDKGraphRequest *request = [[FBSDKGraphRequest alloc] initWithGraphPath:graphPath parameters:nil];
[request startWithCompletionHandler:^(FBSDKGraphRequestConnection *connection, id result, NSError *error) {
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
if (self.cancelled) {
return;
}
if (error) {
[OLFacebookAlbumRequest handleFacebookError:error completionHandler:handler];
return;
}
NSString *parsingErrorMessage = #"Failed to parse Facebook Response. Please check your internet connectivity and try again.";
NSError *parsingError = [NSError errorWithDomain:kOLErrorDomainFacebookImagePicker code:kOLErrorCodeFacebookImagePickerBadResponse userInfo:#{NSLocalizedDescriptionKey: parsingErrorMessage}];
id data = [result objectForKey:#"data"];
if (![data isKindOfClass:[NSArray class]]) {
handler(nil, parsingError, nil);
return;
}
NSMutableArray *albums = [[NSMutableArray alloc] init];
for (id album in data) {
if (![album isKindOfClass:[NSDictionary class]]) {
continue;
}
id albumId = [album objectForKey:#"id"];
id photoCount = [album objectForKey:#"count"];
id name = [album objectForKey:#"name"];
if (!([albumId isKindOfClass:[NSString class]] && [photoCount isKindOfClass:[NSNumber class]]
&& [name isKindOfClass:[NSString class]])) {
continue;
}
OLFacebookAlbum *album = [[OLFacebookAlbum alloc] init];
album.albumId = albumId;
album.photoCount = [photoCount unsignedIntegerValue];
album.name = name;
album.coverPhotoURL = [NSURL URLWithString:[NSString stringWithFormat:#"https://graph.facebook.com/%#/picture?type=small&access_token=%#", album.albumId, [FBSDKAccessToken currentAccessToken].tokenString]];
[albums addObject:album];
}
// get next page cursor
OLFacebookAlbumRequest *nextPageRequest = nil;
id paging = [result objectForKey:#"paging"];
if ([paging isKindOfClass:[NSDictionary class]]) {
id cursors = [paging objectForKey:#"cursors"];
id next = [paging objectForKey:#"next"]; // next will be non nil if a next page exists
if (next && [cursors isKindOfClass:[NSDictionary class]]) {
id after = [cursors objectForKey:#"after"];
if ([after isKindOfClass:[NSString class]]) {
nextPageRequest = [[OLFacebookAlbumRequest alloc] init];
nextPageRequest.after = after;
}
}
}
handler(albums, nil, nextPageRequest);
}];
}
else {
NSString *message = #"No Facebook user authentication found.";
handler(nil, [NSError errorWithDomain:kOLErrorDomainFacebookImagePicker code:kOLErrorCodeFacebookImagePickerNoOpenSession userInfo:#{NSLocalizedDescriptionKey: message}], nil);
}
}
//Code for fetching albums...
- (void)loadNextAlbumPage {
self.inProgressRequest = self.albumRequestForNextPage;
self.albumRequestForNextPage = nil;
[self.inProgressRequest getAlbums:^(NSArray/*<OLFacebookAlbum>*/ *albums, NSError *error, OLFacebookAlbumRequest *nextPageRequest) {
self.inProgressRequest = nil;
self.loadingIndicator.hidden = YES;
self.albumRequestForNextPage = nextPageRequest;
if (error) {
if (self.parentViewController.isBeingPresented) {
self.loadingIndicator.hidden = NO;
self.getAlbumError = error; // delay notification so that delegate can dismiss view controller safely if desired.
} else {
[self.delegate albumViewController:self didFailWithError:error];
}
return;
}
NSMutableArray *paths = [[NSMutableArray alloc] init];
for (NSUInteger i = 0; i < albums.count; ++i) {
[paths addObject:[NSIndexPath indexPathForRow:self.albums.count + i inSection:0]];
}
[self.albums addObjectsFromArray:albums];
if (self.albums.count == albums.count) {
// first insert request
[self.tableView reloadData];
} else {
[self.tableView insertRowsAtIndexPaths:paths withRowAnimation:UITableViewRowAnimationFade];
}
if (nextPageRequest) {
self.tableView.tableFooterView = self.loadingFooter;
} else {
self.tableView.tableFooterView = nil;
}
}];
}
//And when they signed up:
[[[FBSDKGraphRequest alloc] initWithGraphPath:#"me" parameters:#{ #"fields" : #"id,first_name,photos,picture.width(400).height(400)"}]
startWithCompletionHandler:^(FBSDKGraphRequestConnection *connection, id result, NSError *error) {
if (!error) { //etc etc the method continues.
FB authentication will give you a unique access_token for a particular set of permissions. To access user photos, you need to request the user_photos permission. Use the FBSDKLoginButton to request permissions.
loginButton.readPermissions = #[#"public_profile", #"email", #"user_photos"];
Once you have an access token with the required permissions, persist that locally (on the device) to reuse in future. If the access code is still valid, you won't need to request it again. If it becomes invalid (in case the user explicitly revoked permissions to your app), send them back to the login screen.
I am working on passbook feature for creating Boarding pass in iOS, I am able to create a single boarding pass and it is working well, but while tying to create multiple passes, I am getting the following issues:
I am able to get the counts but all passes are being replaced with last one while REVIEW
After selecting SAVE ALL, there is only one pass in passbook app, i.e the last one.
I am using Following code to show passes
-(void) initializeWithMultiPassbookArray:(NSMutableArray *)arrPasses
{
NSMutableArray *arrPKPass = [[NSMutableArray alloc] init];
for(NSString *path in arrPasses)
{
NSData *data;
if(path && path.length)
{
data = [[NSData alloc] initWithContentsOfFile:path];
NSError *error;
PKPass *passSample = [[PKPass alloc] initWithData:data error:&error];
if(passSample)
{
[arrPKPass addObject:passSample];
passSample = nil;
}
else
JALog(#"passSample is nil");
}
else
{
JALog(#"Error occured while fetching pkpass data from cache path");
}
}
if(arrPKPass && arrPKPass.count > 1)
{
if(!self.passLibrary)
self.passLibrary = [[PKPassLibrary alloc] init];
__weak BookingSuccessfulViewController *self_ = self;
if(viewPopUpBg)
{
[viewPopUpBg removeFromSuperview];
viewPopUpBg = nil;
}
[self.passLibrary addPasses:arrPKPass withCompletionHandler:^(PKPassLibraryAddPassesStatus status) {
switch (status) {
case PKPassLibraryDidAddPasses:
{
[App_Delegate performSelectorOnMainThread:#selector(RemoveLoader) withObject:nil waitUntilDone:YES];
if([self_ respondsToSelector:#selector(showAlertFor:withMsg:withTag:)])
[self_ showAlertFor:SAVED withMsg:#"Your booking details have been added to Passbook." withTag:0];
break;
}
case PKPassLibraryDidCancelAddPasses:
{
[App_Delegate performSelectorOnMainThread:#selector(RemoveLoader) withObject:nil waitUntilDone:YES];
break;
}
case PKPassLibraryShouldReviewPasses:
{
//[self_ performSelectorOnMainThread:#selector(RemoveLoader) withObject:nil waitUntilDone:YES];
[App_Delegate performSelectorOnMainThread:#selector(RemoveLoader) withObject:nil waitUntilDone:YES];
PKAddPassesViewController *vcPKAddPass = [[PKAddPassesViewController alloc] initWithPasses:arrPKPass];
[vcPKAddPass setDelegate:(id)self_];
[self_ presentViewController:vcPKAddPass animated:YES completion:nil];
vcPKAddPass = nil;
break;
}
default:
break;
}
}];
}
else if(arrPKPass.count == 1)
{
if(viewPopUpBg)
{
[viewPopUpBg removeFromSuperview];
viewPopUpBg = nil;
}
[App_Delegate performSelectorOnMainThread:#selector(RemoveLoader) withObject:nil waitUntilDone:YES];
PKPass *passLocal = [arrPKPass objectAtIndex:0];
if(passLocal)
{
self.currentPass = passLocal;
//present view controller to add the pass to the library
PKAddPassesViewController *vcPKAddPass = [[PKAddPassesViewController alloc] initWithPass:passLocal];
[vcPKAddPass setDelegate:(id)self];
[self presentViewController:vcPKAddPass animated:YES completion:nil];
vcPKAddPass = nil;
}
}
else
{
JALog(#"arrPKPass is nil");
}
}
Got my ans:
Every Pass should contain different serial No in the key "serialNumber" while creating JSON
Also Check the Capabilities in Project>Target>Capabilities>Passbook
Allow subset of pass types is selected with specific identifiers.
These steps worked for me.
Hopefully help to others also.
Thanks
Hello i'm working on this app which has a login section. What i want is when i click the login button that button has to be unclickable until the login succeeds or fails. I already tried adding this line doLogin.enabled = NO;
But that wasn't useful. Please help. This is my code:
- (IBAction)doLogin:(id)sender {
[self loginUser];
}
- (void)loginUser
{
if (![self.usernameBox.text isEqualToString:#""] && ![self.passwordBox.text isEqualToString:#""])
{
//TODO: check id email pattern is correct
[self showLoginProcess:true];
[[AuthSingleton getInstance] attemptLoginWithUsername:self.usernameBox.text andPassword:self.passwordBox.text withSuccesBlock:^(AFHTTPRequestOperation *operation, id responseObject)
{
[self showLoginProcess:false];
UIViewController *newFrontController = nil;
PaMapViewController * vc = [[PaMapViewController alloc] init];
newFrontController = [[UINavigationController alloc] initWithRootViewController:vc];
SWRevealViewController *revealController = self.revealViewController;
[revealController pushFrontViewController:newFrontController animated:YES];
} andFailureBlock:^(AFHTTPRequestOperation *operation, NSError *error)
{
NSDictionary *dic = [error.userInfo objectForKey:#"JSONResponseSerializerWithDataKey"];
#ifdef DEBUG
NSLog(#"dic = %#", dic);
#endif
if ([[dic objectForKey:#"error_uri"] isEqual:#"phone"])
{
NSError *jsonError;
NSData *objectData = [[dic objectForKey:#"error_description"] dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:objectData
options:NSJSONReadingMutableContainers
error:&jsonError];
[self loginFailed:json];
}
else
{
[self loginFailed:dic];
}
}];
}
else
{
//TODO: show proper message Test
NSLog(#"username or password is empty %#", kBaseURL);
}
}
- (void)showLoginProcess:(BOOL) show
{
[self.spinner setColor:[UIColor whiteColor]];
self.spinner.hidden = !show;
self.usernameBox.hidden = show;
self.passwordBox.hidden = show;
if (show)
{
[self.spinner startAnimating];
} else
{
[self.spinner stopAnimating];
}
}
Hope you have declared a property for the login button. Let it be "doLogin".
What you need to do is
- (IBAction)doLogin:(id)sender
{
[self loginUser];
doLogin.userInteractionEnabled = NO;
}
and when login succeeds or fails write
doLogin.userInteractionEnabled = YES;
within the corresponding block.
Instead of
doLogin.enabled = NO
write
doLogin.userInteractionEnabled = NO
- (IBAction)doLogin:(id)sender
{
doLogin.userInteractionEnabled = NO;
[self loginUser];
doLogin.userInteractionEnabled = YES;
}
Given below is the code snipe that should help
- (IBAction)loginUser:(id)sender
{
//before login disable the user interaction of the button
loginButton.userInteractionEnabled = NO;
/*Your login logic here*/
// after successful login enable the login button once again
loginButton.userInteractionEnabled = YES;
}
I have followed a great tutorial online on how to use Game Center in your iOS apps. It can be found here: http://code.tutsplus.com/tutorials/ios-sdk-game-center-achievements-and-leaderboards-part-2--mobile-5801
However the code for submitting an achievement seems to unlock achievements which have already been unlocked and I don't understand why. Here is my method which deals with a achievements:
-(void)submitAchievement:(NSString*)identifier percentComplete:(double)percentComplete {
if (self.earnedAchievementCache == NULL) {
[GKAchievement loadAchievementsWithCompletionHandler: ^(NSArray *scores, NSError *error) {
if (error == NULL) {
NSMutableDictionary *tempCache = [NSMutableDictionary dictionaryWithCapacity: [scores count]];
for (GKAchievement *score in tempCache) {
[tempCache setObject: score forKey: score.identifier];
}
self.earnedAchievementCache = tempCache;
[self submitAchievement:identifier percentComplete: percentComplete];
}
else {
// Something broke loading the achievement list. Error out, and we'll try again the next time achievements submit.
[self callDelegateOnMainThread: #selector(achievementSubmitted:error:) withArg: NULL error: error];
}
}];
}
else {
// Search the list for the ID we're using...
GKAchievement *achievement = [self.earnedAchievementCache objectForKey:identifier];
if (achievement != NULL) {
if ((achievement.percentComplete >= 100.0) || (achievement.percentComplete >= percentComplete)) {
// Achievement has already been earned so we're done.
achievement = NULL;
}
achievement.percentComplete = percentComplete;
}
else {
achievement = [[[GKAchievement alloc] initWithIdentifier:identifier] autorelease];
achievement.percentComplete = percentComplete;
// Add achievement to achievement cache...
[self.earnedAchievementCache setObject:achievement forKey:achievement.identifier];
}
if (achievement != NULL) {
// Submit the Achievement...
[achievement reportAchievementWithCompletionHandler: ^(NSError *error) {
[self callDelegateOnMainThread:#selector(achievementSubmitted:error:) withArg:achievement error:error];
}];
}
}
}
Thanks for your time, Dan.
Can you try this code and see the log. I added NSLog statements to see if the code detects achievement is completed and sets the achievement to nil. Also delete NULL from the code. Let me know how it works out.
-(void)submitAchievement:(NSString*)identifier percentComplete:(double)percentComplete {
if (!self.earnedAchievementCache) {
[GKAchievement loadAchievementsWithCompletionHandler: ^(NSArray *scores, NSError *error) {
if (!error) {
NSMutableDictionary *tempCache = [NSMutableDictionary dictionaryWithCapacity: [scores count]];
for (GKAchievement *score in scores) { // the error is here
[tempCache setObject: score forKey: score.identifier];
}
self.earnedAchievementCache = tempCache;
[self submitAchievement:identifier percentComplete: percentComplete];
}
else {
// Something broke loading the achievement list. Error out, and we'll try again the next time achievements submit.
[self callDelegateOnMainThread: #selector(achievementSubmitted:error:) withArg: NULL error: error];
}
}];
}else {
// Search the list for the ID we're using...
GKAchievement *achievement = [self.earnedAchievementCache objectForKey:identifier];
NSLog(#"achievement %f",achievement.percentComplete);
if (achievement) {
if ((achievement.percentComplete >= 100.0) || (achievement.percentComplete >= percentComplete)) {
NSLog(#"Achievement has already been earned so we're done.");
achievement = nil;
}else{
achievement.percentComplete = percentComplete;
}
}else {
achievement = [[[GKAchievement alloc] initWithIdentifier:identifier] autorelease];
achievement.percentComplete = percentComplete;
// Add achievement to achievement cache...
[self.earnedAchievementCache setObject:achievement forKey:achievement.identifier];
}
if (achievement) {
NSLog(#"Submit the Achievement...");
[achievement reportAchievementWithCompletionHandler: ^(NSError *error) {
[self callDelegateOnMainThread:#selector(achievementSubmitted:error:) withArg:achievement error:error];
}];
}
}
}
Has anyone found the replacement for
[GKAchievement reportAchievementWithCompletionHandler]?
Typically when things are deprecated the docs indicate a replacement. Not so with this one so far and I wanted to cross this off the list of possible causes of another issue we are seeing.
Was looking for the same info and saw your post, here is what I went with after not finding anything either:
NSArray *achievements = [NSArray arrayWithObjects:achievement, nil];
[GKAchievement reportAchievements:achievements withCompletionHandler:^(NSError *error) {
if (error != nil) {
NSLog(#"Error in reporting achievements: %#", error);
}
}];
Here is apple's full code (same/similar to Silly Goose's Answer)
- (void) completeMultipleAchievements
{
GKAchievement *achievement1 = [[GKAchievement alloc] initWithIdentifier: #"DefeatedFinalBoss"];
GKAchievement *achievement2 = [[GKAchievement alloc] initWithIdentifier: #"FinishedTheGame"];
GKAchievement *achievement3 = [[GKAchievement alloc] initWithIdentifier: #"PlayerIsAwesome"];
achievement1.percentComplete = 100.0;
achievement2.percentComplete = 100.0;
achievement3.percentComplete = 100.0;
NSArray *achievementsToComplete = [NSArray arrayWithObjects:achievement1,achievement2,achievement3, nil];
[GKAchievement reportAchievements: achievementsToComplete withCompletionHandler:^(NSError *error)
{
if (error != nil)
{
NSLog(#"Error in reporting achievements: %#", error);
}
}];
}
This works in iOS7 with no issues.
- (void)checkAchievements
{
if(myScore >= 25000){
GKAchievement *achievement= [[GKAchievement alloc] initWithIdentifier:#"Achiev1"];
achievement.percentComplete = 100.0;
achievement.showsCompletionBanner = YES;
[self Achievements:achievement];
}
}
-(void)Achievements:(GKAchievement*)achievement {
NSArray *achievements = [NSArray arrayWithObjects:achievement, nil];
[GKAchievement reportAchievements:achievements withCompletionHandler:^(NSError *error) {
if (error != nil) {
NSLog(#"Error in reporting achievements: %#", error);
}
}];
}