Game Center inviting friends programmatically - iphone

I'm facing difficulty inviting a friend to the match.
GKMatchRequest *request = [[[GKMatchRequest alloc] init] autorelease];
request.minPlayers = 2;
request.maxPlayers = 2;
request.playersToInvite = [NSArray arrayWithObjects: #"G:1102359306",nil ];
// GKMatchmakerViewController *mv = [[GKMatchmakerViewController alloc] initWithMatchRequest:request];
// [self presentModalViewController:mv animated:YES];
[[GKMatchmaker sharedMatchmaker] findMatchForRequest:request withCompletionHandler:^(GKMatch *match, NSError *error) {
if (error) {
NSLog([error description]);
}
else if (match != nil) {NSLog(#"good match");
//self.chatMatch = match;
//self.chatMatch.delegate = self;
//[self chatReady];
}
else {
NSLog(#"other error");
}
}];
The problem is I never receive the invitation notification on second device logged to the account - G:1102359306.
When I use GKMatchmakerViewController (uncomment above 2 lines) and comment GKMatchmaker block I have automatically checked the good friend - G:1102359306 and when I invites him the notification with accept/decline is shown, that's how I know it's correct.
Do you see anything wrong with the code above? I want to use my own UI to handle the multiplayer mode. The strange problem is I also don't see in console any logs good match/other error, and the [error description] is only printed when I call the above code twice - it says that the previous req was canceled.

You cannot programmatically invite a specific set of players to a match. The findMatchForRequest:withCompletionHandler: documentation says this:
The match request’s playersToInvite property is ignored; to invite a specific set of players to the match, you must display a matchmaker view controller.
There is no public API that does what you want.

Related

How to get local player score from Game Center

How to get score of local player from Leaderboard Game Center? I tried this code, but it returns nothing. Anybody know how to solve it, or is there better way how to get score?
- (NSString*) getScore: (NSString*) leaderboardID
{
__block NSString *score;
GKLeaderboard *leaderboardRequest = [[GKLeaderboard alloc] init];
if (leaderboardRequest != nil)
{
leaderboardRequest.identifier = leaderboardID;
[leaderboardRequest loadScoresWithCompletionHandler: ^(NSArray *scores, NSError *error) {
if (error != nil)
{
NSLog(#"%#", [error localizedDescription]);
}
if (scores != nil)
{
int64_t scoreInt = leaderboardRequest.localPlayerScore.value;
score = [NSString stringWithFormat:#"%lld", scoreInt];
}
}];
}
return score;
}
I think, that method have to wait for completion of [leaderboardRequest loadScoresWithCompletionHandler: ...
Is it possible?
Your code appears to not have any bugs that I can see. I would recommend displaying the standard leaderboard interface to see if your code that reports the scores is actually working correctly. If so, you should see the scores in the leaderboard. The code below works in my game, and I know the score reporting is working properly because it shows in the default game center UI.
GKLeaderboard *leaderboardRequest = [[GKLeaderboard alloc] init];
leaderboardRequest.identifier = kLeaderboardCoinsEarnedID;
[leaderboardRequest loadScoresWithCompletionHandler:^(NSArray *scores, NSError *error) {
if (error) {
NSLog(#"%#", error);
} else if (scores) {
GKScore *localPlayerScore = leaderboardRequest.localPlayerScore;
CCLOG(#"Local player's score: %lld", localPlayerScore.value);
}
}];
If you aren't sure how, the code below should work to show the default leaderboard (iOS7):
GKGameCenterViewController *gameCenterVC = [[GKGameCenterViewController alloc] init];
gameCenterVC.viewState = GKGameCenterViewControllerStateLeaderboards;
gameCenterVC.gameCenterDelegate = self;
[self presentViewController:gameCenterVC animated:YES completion:^{
// Code
}];
You cannot return score outside the block. In this code first "return score" will be executed before the method "loadScoresWithCompletionHandler". Additionally you haven't set initial value for "score", this method will return completely random value.
I suggest you to put your appropriate code inside the block, instead of:
int64_t scoreInt = leaderboardRequest.localPlayerScore.value;
score = [NSString stringWithFormat:#"%lld", scoreInt];
The leaderboard request completes after the return of your method. This means that you are returning a null string.
The method you put the leaderboard request in should be purely for sending the request. The method will be finished executing before the leaderboard request is complete thus your "score = [NSString stringWithFormat:#"%lld", scoreInt];" line is being executed AFTER the return of the "score" which is null until that line is executed.
The solution is to not return the outcome of the completion handler using the method that sends the request. The score is definitely being retrieved correctly, so just do whatever you need to with the score inside of the completion handler. You have no way to know when the completion handler is going to be executed. This, in fact, is the reason why Apple allows you to store the code to be executed in a block! Although, it can be confusing to understand how to work with blocks that will definitely be executed later, or in your situation, sometime after the method is returning.
The best way to handle your situation is to not return anything in this method and just use the "score" variable as you intend to after the block has set score to a non-null value!

Why can't I authenticate to Facebook Chat in my iOS application?

I have been scouring the internet trying to find a solution to this problem. I understand the basic idea on how it is supposed to work, but I can't get the implementation to work and I can't find any decent examples to help me. So far I have successfully been able to log in a user using the iOS 6 authentication mechanism, but I cannot figure out how to authenticate a user to the Jabber server from there. Here is what I have:
After the user has logged in connect is called
-(void)connect
{
[self setupStream];
NSError *error = nil;
[_xmppStream authenticateWithFacebookAccessToken: FBSession.activeSession.accessTokenData.accessToken error:&error];
NSLog(#"%#", error);
[NSString stringWithFormat:#"%#", self];
}
-(void)newSetupStream
{
_xmppStream = [[XMPPStream alloc] initWithFacebookAppId:#"611051652253156"];
#if !TARGET_IPHONE_SIMULATOR
{
xmppStream.enableBackgroundingOnSocket = YES;
}
#endif
_xmppReconnect = [[XMPPReconnect alloc] init];
_xmppRosterStorage = [[XMPPRosterCoreDataStorage alloc] init];
_xmppRoster = [[XMPPRoster alloc] initWithRosterStorage:_xmppRosterStorage];
_xmppRoster.autoFetchRoster = YES;
_xmppRoster.autoAcceptKnownPresenceSubscriptionRequests = YES;
_xmppvCardStorage = [XMPPvCardCoreDataStorage sharedInstance];
_xmppvCardTempModule = [[XMPPvCardTempModule alloc] initWithvCardStorage:_xmppvCardStorage];
_xmppvCardAvatarModule = [[XMPPvCardAvatarModule alloc] initWithvCardTempModule:_xmppvCardTempModule];
_xmppCapabilitiesStorage = [XMPPCapabilitiesCoreDataStorage sharedInstance];
_xmppCapabilities = [[XMPPCapabilities alloc] initWithCapabilitiesStorage:_xmppCapabilitiesStorage];
_xmppCapabilities.autoFetchHashedCapabilities = YES;
_xmppCapabilities.autoFetchNonHashedCapabilities = NO;
[_xmppReconnect activate:_xmppStream];
[_xmppRoster activate:_xmppStream];
[_xmppvCardTempModule activate:_xmppStream];
[_xmppvCardAvatarModule activate:_xmppStream];
[_xmppCapabilities activate:_xmppStream];
[_xmppStream addDelegate:self delegateQueue:dispatch_get_main_queue()];
[_xmppRoster addDelegate:self delegateQueue:dispatch_get_main_queue()];
}
No matter what I try it always returns the same error: Error Domain=XMPPStreamErrorDomain Code=4 "The server does not support X-FACEBOOK-PLATFORM authentication."
I am not incredibly familiar with either XMPP or the Facebook API so I'm sure there is something simple I am missing, but I have been working on this forever and been unable to make any progress. Any ideas?
I found my answer. In case anyone else runs into this same problem: I never actually told my XMPPStream to connect. I had tried all of the connect methods before, but they never worked. I now realize that I simply did not wait for them to finish connecting. Here is what I ended up changing:
NSError *error;
NSError *err;
[_xmppStream connectWithTimeout:10.00 error:&err];
[_xmppStream authenticateWithFacebookAccessToken: FBSession.activeSession.accessTokenData.accessToken error:&error];
while (error)
{
sleep(1);
[_xmppStream authenticateWithFacebookAccessToken: FBSession.activeSession.accessTokenData.accessToken error:&error];
}
I realize that it's not the most elegant solution, but it works.
make sure you use chat.facebook.com or facebook.com as the host name.
You must also open session with xmpp_login permission.
For me the above host name worked 100% fine

GKTurnBasedMatch match-making waiting until another player matched

I find the default turn based matchmaking process unintuitive for users. The matchmaker returns a match even when the other player is not yet found and then local user has to take turn in a fresh game after which he/she has to wait in game screen. Instead of that I would rather have the users stay in an interactive screen where they can cancel their match request or wait until they are matched (then I decide who starts first). Is there any way to do this, not a workaround but a robust one?
My current code, doing nothing towards my purpose, is below:
-(IBAction)gcMatchPressed:(id)sender
{
GKMatchRequest *request = [[GKMatchRequest alloc] init];
request.minPlayers = 2;
request.maxPlayers = 2;
request.playerGroup = PLAYERGROUP;
[GKTurnBasedMatch findMatchForRequest:request withCompletionHandler:^(GKTurnBasedMatch *match, NSError *error) {
if (match != nil)
{
self.gcMatch = match;
[self performSegueWithIdentifier:#"Multiplayer" sender:self];
}
} ];
}

Problems With Game Center

Just finished my first iOS game (mostly). Its all ready to go, just need to link in Game Center. This however, is proving to be ...annoying. My App is all registered on iTunes Connect, and all the Game Center code is taken directly from Apple. So why is NONE of this working?
First: authentication always fails;
GKLocalPlayer *localPlayer = [GKLocalPlayer localPlayer];
localPlayer.authenticateHandler = ^(UIViewController *loginVC, NSError *error) {
if ([GKLocalPlayer localPlayer].authenticated) {
// authentication successful
}
else if (loginVC) {
// player not logged in yet, present the vc
[self presentViewController:loginVC animated:YES completion:nil];
}
else {
// authentication failed
}
};
With error "The requested operation has been cancelled"
As a corollary, trying to present a game center vc in the future yeilds a "Player is not signed in message"
Second: this
GKScore *score = [[GKScore alloc] init];
if (score) {
score.value = self->selectedScore;
NSArray* scoreArray = [[NSArray alloc]initWithObjects:score, nil];
//score.category = #"High Scores";
UIActivityViewController *avc = [[UIActivityViewController alloc]
initWithActivityItems:scoreArray applicationActivities:nil];
avc.completionHandler = ^(NSString *activityType, BOOL completed) {
if (completed)
[self dismissViewControllerAnimated:YES completion:nil];
};
if (avc)
[self presentViewController:avc animated:YES completion:nil];
}
yeilds a 'NSInvalidArgumentException', reason: '*** -[__NSPlaceholderArray initWithObjects:count:]: attempt to insert nil object from objects[0]'
What am I fundamentally doing wrong? Thanks to anyone who can offer any help.
Try to log out/in in the Game Center application with your test user account.
Check this answer: https://stackoverflow.com/a/4420457/89364

Open Twitter Setting from ACAccountStore (iOS 5.1 TWITTER)

in iOS 5.0 i was opening Twitter setting from my app by
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:#"prefs:root=TWITTER"]];
but , this features is removed in iOS 5.1 , hence i can not able to open twitter setting .
Now i am using
+ (void)makeRequestsWithURL: (NSURL *)url {
// Create an account store object.
ACAccountStore *accountStore = [[ACAccountStore alloc] init];
// Create an account type that ensures Twitter accounts are retrieved.
ACAccountType *accountType = [accountStore accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierTwitter];
[self canTweetStatus];
// Request access from the user to use their Twitter accounts.
[accountStore requestAccessToAccountsWithType:accountType withCompletionHandler:^(BOOL granted, NSError *error) {
if(granted) {
// Get the list of Twitter accounts.
NSArray *accountsArray = [accountStore accountsWithAccountType:accountType];
// For the sake of brevity, we'll assume there is only one Twitter account present.
// You would ideally ask the user which account they want to tweet from, if there is more than one Twitter account present.
if ([accountsArray count] > 0) {
// Grab the initial Twitter account to tweet from.
ACAccount *twitterAccount = [accountsArray objectAtIndex:0];
// Create a request, which in this example, posts a tweet to the user's timeline.
// This example uses version 1 of the Twitter API.
// This may need to be changed to whichever version is currently appropriate.
TWRequest *postRequest = [[TWRequest alloc] initWithURL:url parameters:nil requestMethod:TWRequestMethodPOST];
// Set the account used to post the tweet.
[postRequest setAccount:twitterAccount];
// Perform the request created above and create a handler block to handle the response.
[postRequest performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error) {
NSString *output = [NSString stringWithFormat:#"HTTP response status: %i", [urlResponse statusCode]];
iOS5Twitter *twitter5 = [[iOS5Twitter alloc] init];
[twitter5 performSelectorOnMainThread:#selector(displayText:) withObject:output waitUntilDone:NO];
[twitter5 release]; }];
}
}
}];
}
for making request, i am able to check wether i am loged in or not by the
if ([TWTweetComposeViewController canSendTweet])
but now i want : if i am not loged in it would be show an alert like shown in image and want to move to the twitter setting . is it possible ? or i have to manually go ti twitter setting ?
It is little tricky , i get by the removing the subviews in *TWTWeetComposeViewController*, so it shows only alert when user is not loged in and by the clicking on setting button , we can open Setting page in my app.
+ (void)setAlertForSettingPage :(id)delegate
{
// Set up the built-in twitter composition view controller.
TWTweetComposeViewController *tweetViewController = [[TWTweetComposeViewController alloc] init];
// Create the completion handler block.
[tweetViewController setCompletionHandler:^(TWTweetComposeViewControllerResult result) {
[delegate dismissModalViewControllerAnimated:YES];
}];
// Present the tweet composition view controller modally.
[delegate presentModalViewController:tweetViewController animated:YES];
//tweetViewController.view.hidden = YES;
for (UIView *view in tweetViewController.view.subviews){
[view removeFromSuperview];
}
}
here , deleate is your viewcontroller , if you are using this method inside your viewcontroller just use self instead of delegate.
iOS 6 uses SLComposeViewController instead of TWTweetComposeViewController, so now you have to do this if you want it to work on iOS 6 and iOS 5:
UIViewController *tweetComposer;
if([SLComposeViewController class] != nil)
{
tweetComposer = [SLComposeViewController composeViewControllerForServiceType:SLServiceTypeTwitter];
[(SLComposeViewController *)tweetComposer setCompletionHandler:^(SLComposeViewControllerResult result)
{
// do whatever you want
}];
}
else
{
tweetComposer = [[TWTweetComposeViewController alloc] init];
[(TWTweetComposeViewController *)tweetComposer setCompletionHandler:^(TWTweetComposeViewControllerResult result)
{
// do whatever you want
}];
}
for (UIView *view in [[tweetComposer view] subviews])
[view removeFromSuperview];
[self presentViewController:tweetComposer animated:NO completion:nil];
Building upon Senior's and PJR's answers above, this is what worked for me.
Same as Senior's, except with a dimissViewController in the completion handler. For me this got around the problem that the empty view controller would stick around after I returned to the app from the settings. It would overlay my view controller and make it useless.
Brilliant solution, thanks Senior and PJR.
UIViewController *tweetComposer;
if([SLComposeViewController class] != nil)
{
tweetComposer = [SLComposeViewController composeViewControllerForServiceType:SLServiceTypeTwitter];
[(SLComposeViewController *)tweetComposer setCompletionHandler:^(SLComposeViewControllerResult result)
{
[self dismissViewControllerAnimated:NO completion:nil];
}];
}
else
{
tweetComposer = [[TWTweetComposeViewController alloc] init];
[(TWTweetComposeViewController *)tweetComposer setCompletionHandler:^(TWTweetComposeViewControllerResult result)
{
[self dismissViewControllerAnimated:NO completion:nil];
}];
}
for (UIView *view in [[tweetComposer view] subviews])
[view removeFromSuperview];
[self presentViewController:tweetComposer animated:NO completion:nil];
To remove the post view,
instead using this code:
for (UIView *view in [[tweetComposer view] subviews])
[view removeFromSuperview];
use this:
tweetComposer.view.alpha = 0;
works both for twitter and facebook.