Can you help me how can I get user info.
NSString *name;
[FBRequestConnection startForMeWithCompletionHandler:^(FBRequestConnection *connection, id result, NSError *error)
{
if (!error)
{
// Success! Include your code to handle the results here
name = [result objectForKey:#"first_name"]; // Error!!! how to get data from this handler
}
else
{
// An error occurred, we need to handle the error
// See: https://developers.facebook.com/docs/ios/errors
}
}];
Code described above - asynchronous? How to make it synchronous? Tell me about the mechanism or tell me where to read. Thank you!
You could read everything about Facebook SDK on this site: https://developers.facebook.com.
They don't provide synchronous API and I don't even know why you might need it. But if you really do you can do some workaround. See implementation:
__block id result = nil;
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
[FBRequestConnection startForMeWithCompletionHandler:^(FBRequestConnection *connection, id theResult, NSError *error) {
result = theResult;
dispatch_semaphore_signal(semaphore); // UPDATE: dispatch_semaphore_wait call was here, which is wrong
}];
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(#"%#", result);// this code will be launched after you've received response from Facebook
Result conforms FBGraphUser protocol. So if you it doesn't contain value for first_name key, user wouldn't have specified it. You could print result in debugger and see what it is.
Seems like you totally mess up. Try this code, but be sure, your understand it:
- (void) SetUserData
{
[FBRequestConnection startForMeWithCompletionHandler:^(FBRequestConnection *connection, id result, NSError *error)
{
if (!error)
{
self.first_name = [result objectForKey:#"first_name"];
NSLog(#"First name just came from server: first_name: %#", self.first_name);
}
else
{
}
}];
[self furtherProcessMethod];
}
- (void) furtherProcessMethod
{
if (self.first_name == nil)
{
// this is not recursion, do not miss that!!
[self performSelector:#selector(furtherProcessMethod) withObject:nil afterDelay:2.0];
}
else
{
NSLog(#"First name that I can work with first_name: %#", self.first_name);
// here you can handle you first_name, after it came
}
}
So after some time you need to get in log:
First name just came from server: first_name: some name
First name that I can work with first_name: some name
You can use additional method to solve your problem:
1) make name as property: __block NSString *name
2) move you code, that you need to perform, to separate method, like that:
- (void) methodWithComplitionHandler
{
name = nil;
[FBRequestConnection startForMeWithCompletionHandler:^(FBRequestConnection *connection, id result, NSError *error)
{
if (!error)
{
name = [result objectForKey:#"first_name"];
}
else
{
}
}];
[self futherProcessMethod];
}
- (void) furtherProcessMethod
{
if (self.name == nil)
{
[self performSelector:#selector(furtherProcessMethod) withObject:nil afterDelay:3.0]; // here set appropriate delay
}
else
{
// do some with name;
}
}
As I clarify in previous answer, you need to work with name inside "else" statement. Please, debug for more understanding. So we have:
- (void) SetUserData
{
[FBRequestConnection startForMeWithCompletionHandler:^(FBRequestConnection *connection, id result, NSError *error)
{
if (!error)
{
self.first_name = [result objectForKey:#"first_name"];
}
else
{
}
}];
[self furtherProcessMethod];
}
- (void) furtherProcessMethod
{
if (self.first_name == nil)
{
[self performSelector:#selector(furtherProcessMethod) withObject:nil afterDelay:30.0]; // here set appropriate delay
}
else
{
NSLog(#"first_name: %#", self.first_name);
}
}
Related
I have a login view. When user clicks "login" button, the app gonna authenticate the account then decide to segue or not. However, the POST request is asynchronous.
- (BOOL)shouldPerformSegueWithIdentifier:(NSString *)identifier sender:(id)sender
is executed before I can get something from the server. Here is my codeļ¼
- (void)isAuthenticaionConfirmed
{
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
NSDictionary *parameters = #{#"account":self.studentsNumber.text,
#"password":self.passwordToJW.text};
[manager POST:checkAutenticationURL parameters:parameters success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"JSON: %#", responseObject);
[self parseDictionary:responseObject];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
[self showAlert:#"3"];
}];
}
- (void)parseDictionary: (NSDictionary *)dictionary
{
NSString *errorInfo = dictionary[#"err"];
if ([errorInfo isEqualToString:#"subsequent request failed"]) {
[self showAlert:#"3"];
} else if ([errorInfo isEqualToString:#"login failed"]) {
[self showAlert:#"2"];
} else {
_name = dictionary[#"name"];
}
}
here is the code relating to segue
- (BOOL)shouldPerformSegueWithIdentifier:(NSString *)identifier sender:(id)sender
{
if (_name) {
return YES;
} else {
return NO;
}
}
The _name here is the JSON result when the user uses correct account and password.
I solve it by refusing any kinds of segue first
- (BOOL)shouldPerformSegueWithIdentifier:(NSString *)identifier sender:(id)sender
{
return NO;
}
then calling [self performSegueWithIdentifier:#"filterSegue" sender:self] method in paresDictionary
It looks weird. If anyone has a better solution, welcome to leave a comment! :-)
I have already configured all Game Center functions and below code i am using to unlock achievement, which is perfectly working fine.
- (void) unlockAchievementThis:(NSString*)achievementID {
GKAchievement *achievement = [[GKAchievement alloc] initWithIdentifier:
achievementID];
if (achievement){
achievement.percentComplete = 100;
achievement.showsCompletionBanner = true;
[GKAchievement reportAchievements:#[achievement] withCompletionHandler:^(NSError *error) {
if (error != nil) {
NSLog(#"Error at unlockAchievementThis()");
}
}];
}
}
Now My problem is with incremental achievements. I have another method for few achievements and I want the previous achievement percentage to increase it with a constant.
My game is in cpp and i don't know much ObjC.
I got some code below which i think should help me but i don't know how to use achievementDescriptions to get percentage and add incStep into it and submit it to back game center.
- (void) incrementAchievementThis:(NSString*)achievementID :(NSInteger) incStep
{
NSMutableDictionary *achievementDescriptions = [[NSMutableDictionary alloc] init];
[GKAchievementDescription loadAchievementDescriptionsWithCompletionHandler:^(NSArray *descriptions, NSError *error) {
if (error != nil) {
NSLog(#"Error getting achievement descriptions: %#", error);
}
for (GKAchievementDescription *achievementDescription in descriptions) {
[achievementDescriptions setObject:achievementDescription forKey:achievementDescription.identifier];
}
}];
Percentages are stored in GKAchievement percentComplete, so you need to load (and update and report) GKAchievements instead of GKAchievementDescriptions.
GKAchievmenentDescriptions are configured in iTunes Connect and are "read-only" from the point of view of your app.
Finally i got output by below code...
- (void) incrementAchievementThis:(NSString*)achievementID :(NSInteger) incStep
{
[GKAchievement loadAchievementsWithCompletionHandler:^(NSArray *achievements, NSError *error)
{
if (error == nil) {
for (GKAchievement* achievement in achievements) {
if ([achievementID isEqualToString:achievement.identifier]) {
achievement.percentComplete += incStep;
[GKAchievement reportAchievements:#[achievement] withCompletionHandler:^(NSError *error) {
if (error != nil) {
NSLog(#"Error at incrementAchievementThis()");
}
}];
}
}
}
else {
NSLog(#"Error in loading achievements: %#", error);
}
}];
}
I found many tutorials online how to share image on twitter from an iOS app. But i want to know 2 things about social sharing with twitter-
If i post an image on twitter via my app, Can i get image id from twitter in callback method/block? If yes then how?
If i fetch favourites of a user, is the response include text posted with that image? I checked for the same on twitter Rest API doc that there is a text property returned in the response.
Now my question is that if i post some text with image via my iOS app and later make this post favourite in twitter app and now i get my favourites list through twitter rest API in my app, does the text property in the response is same that i posted with my post?
Edit about #1 above:- from SLComposeViewControllerResult docs i found that completion handler return one of
typedef NS_ENUM (NSInteger,
SLComposeViewControllerResult ) {
SLComposeViewControllerResultCancelled,
SLComposeViewControllerResultDone
};
constant so there is no info about image just posted. Am i right? If not please give me some reference about how to get image id please.
Here I have customize alertView,NSLog,etc. You ignore that.
Here is the code to share to twitter by using STTwitter library
- (void)shareToTwitter
{
APP_DELEGATE.navController = self.navigationController;
NSString *strTwitterToken = [[NSUserDefaults standardUserDefaults] objectForKey:#"TwitterToken"];
NSString *strTwitterTokenSecret = [[NSUserDefaults standardUserDefaults] objectForKey:#"TwitterTokenSecret"];
if (strTwitterToken && strTwitterTokenSecret)
{
self.twitter = [STTwitterAPI twitterAPIWithOAuthConsumerKey:TwitterConsumerKey consumerSecret:TwitterSecretKey oauthToken:strTwitterToken oauthTokenSecret:strTwitterTokenSecret];
[self.twitter verifyCredentialsWithSuccessBlock:^(NSString *username) {
DLogs(#"Twitter User Name");
[self twitterMediaUpload];
} errorBlock:^(NSError *error) {
DLogs(#"-- error: %#", error);
[AppConstant showAutoDismissAlertWithMessage:[error localizedDescription] onView:self.view];
[self safariLoginTwitter];
}];
}
else
{
[self safariLoginTwitter];
}
}
-(void)safariLoginTwitter
{
// [APP_CONSTANT getNativeTwitterAccountAccessToken:^(id result) {
//
// }];
self.twitter = [STTwitterAPI twitterAPIWithOAuthConsumerKey:TwitterConsumerKey
consumerSecret:TwitterSecretKey];
[self.twitter postTokenRequest:^(NSURL *url, NSString *oauthToken) {
DLogs(#"-- url: %#", url);
DLogs(#"-- oauthToken: %#", oauthToken);
[[UIApplication sharedApplication] openURL:url];
} authenticateInsteadOfAuthorize:NO
forceLogin:#(YES)
screenName:nil
oauthCallback:#"myapp://twitter_access_tokens/"
errorBlock:^(NSError *error) {
DLogs(#"-- error: %#", error);
[AppConstant showAutoDismissAlertWithMessage:[error localizedDescription] onView:self.view];
}];
}
- (void)setOAuthToken:(NSString *)token oauthVerifier:(NSString *)verifier {
[self.twitter postAccessTokenRequestWithPIN:verifier successBlock:^(NSString *oauthToken, NSString *oauthTokenSecret, NSString *userID, NSString *screenName) {
DLogs(#"-- screenName: %#", screenName);
/*
At this point, the user can use the API and you can read his access tokens with:
_twitter.oauthAccessToken;
_twitter.oauthAccessTokenSecret;
You can store these tokens (in user default, or in keychain) so that the user doesn't need to authenticate again on next launches.
Next time, just instanciate STTwitter with the class method:
+[STTwitterAPI twitterAPIWithOAuthConsumerKey:consumerSecret:oauthToken:oauthTokenSecret:]
Don't forget to call the -[STTwitter verifyCredentialsWithSuccessBlock:errorBlock:] after that.
*/
[[NSUserDefaults standardUserDefaults] setObject:self.twitter.oauthAccessToken forKey:#"TwitterToken"];
[[NSUserDefaults standardUserDefaults] setObject:self.twitter.oauthAccessToken forKey:#"TwitterTokenSecret"];
[[NSUserDefaults standardUserDefaults] synchronize];
[self twitterMediaUpload];
} errorBlock:^(NSError *error) {
[AppConstant showAutoDismissAlertWithMessage:[error localizedDescription] onView:self.view];
DLogs(#"-- %#", [error localizedDescription]);
}];
}
-(void)twitterMediaUpload
{
// ProfileImageBO *objProfImg = nil;
//
// if ([self.objProfile.arrUserImages count]) {
// objProfImg = [self.objProfile.arrUserImages objectAtIndex:0];
// }
[APP_CONSTANT showLoaderWithTitle:#"posting" onView:self.view];
// NSURL *urlProfImg = [NSURL URLWithString:[objProfImg.imageUrl stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
NSURL *screenshotUrl = [self getScreenshotUrl];
[self.twitter postMediaUpload:screenshotUrl uploadProgressBlock:^(NSInteger bytesWritten, NSInteger totalBytesWritten, NSInteger totalBytesExpectedToWrite) {
DLogs(#"uploading");
} successBlock:^(NSDictionary *imageDictionary, NSString *mediaID, NSString *size) {
DLogs(#"imageDictionary = %#, mediaID = %#, size %#",imageDictionary.description,mediaID,size);
[self postToTheTwitterWithMediaId:mediaID];
} errorBlock:^(NSError *error) {
DLogs(#"Error in uploading media, try again ...");
[APP_CONSTANT hideLoader];
[AppConstant showAutoDismissAlertWithMessage:error.localizedDescription onView:self.view];
}];
}
-(void)postToTheTwitterWithMediaId:(NSString *)mediaID
{
NSString *msg = [NSString stringWithFormat:#"Check out My Profile"];
[self.twitter postStatusUpdate:msg inReplyToStatusID:nil mediaIDs:[NSArray arrayWithObject:mediaID] latitude:nil longitude:nil placeID:nil displayCoordinates:nil trimUser:nil successBlock:^(NSDictionary *status) {
DLogs(#"Description %#",status.description);
[self showNotificationToastWithMessage:TwitterPostSuccess];
[APP_CONSTANT hideLoader];
} errorBlock:^(NSError *error) {
DLogs(#"Twitter posting error %#",error.description);
[APP_CONSTANT hideLoader];
[AppConstant showAutoDismissAlertWithMessage:error.localizedDescription onView:self.view];
}];
}
For your second question: Yes, you will get the same text in the response
And This is the code to get favorite list
-(void)getFavListTwitter
{
[self.twitter getFavoritesListWithSuccessBlock:^(NSArray *statuses) {
DLogs(#"%#",statuses.description);
} errorBlock:^(NSError *error) {
DLogs(#"%#",error.description);
}];
}
Say I have a PFObject which I am editing. At later stage I wish to cancel the changes I have done to the PFObject. How do I revert back to original copy of PFObject?
What I have tried
if (self.request.isDirty) { // self.request is a PFObject
// Reload object
NSLog(#"%#", self.request.requestTitle); // Logs ABC, Original was DEF
[self.request refreshInBackgroundWithBlock:^(PFObject *object, NSError *error) {
if (!error) {
NSLog(#"%#", self.request.requestTitle); // Logs ABC
[self.requestDetailsTableView reloadData];
}
}];
}
I tried fetching the object as well, but same result
if (self.request.isDirty) { // self.request is a PFObject
// Reload object
NSLog(#"%#", self.request.requestTitle); // Logs ABC, Original was DEF
[self.request fetchInBackgroundWithBlock:^(PFObject *object, NSError *error) {
if (!error) {
NSLog(#"%#", self.request.requestTitle); // Logs ABC
[self.requestDetailsTableView reloadData];
}
}];
}
There is now a revert method on PFObject which will reset your object back to the server state as long as you haven't called save: http://parseplatform.org/Parse-SDK-iOS-OSX/api/Classes/PFObject.html#/c:objc(cs)PFObject(im)revert
For example, I use a pushed view controller with a form to edit properties on my PFObject subclass and I have two buttons for Cancel and Save...
#IBAction override func cancelAction() {
self.myObject?.revert()
self.navigationController?.popViewControllerAnimated(true)
}
#IBAction override func saveAction() {
self.myObject?.saveInBackgroundWithBlock({ (succeeded: Bool, error: NSError?) -> Void in
if (succeeded) {
self.navigationController?.popViewControllerAnimated(true)
} else {
// Show error
}
})
}
There isn't currently a way to do this directly - you need to do a little fancy footwork with your calls :)
Try this:
if (self.request.isDirty) { // self.request is a PFObject
// Reload object
NSLog(#"%#", self.request.requestTitle); // Logs ABC, Original was DEF
PFQuery *newRequest = [PFQuery queryWithClassName:#"YourClassName"];
//Add your request parameters here...
[newRequest getFirstObjectInBackgroundWithBlock:^(PFObject *object, NSError *error) {
if (!error)
self.request = object;
NSLog(#"%#", self.request.requestTitle); // Should log DEF
[self.requestDetailsTableView reloadData];
}
}];
}
I've been struggling on this for several days so any would be appreciated. I'm trying to save the players array below and display it in a UITableView. I'd like to save it so I can display the local player's friends. I've tried several different things but something that looks it's working for others is this.
__block NSArray *friends;
- (void) loadPlayerData: (NSArray *) identifiers {
[GKPlayer loadPlayersForIdentifiers:identifiers withCompletionHandler:^(NSArray *players, NSError *error) {
if (error != nil) {
// Handle the error.
}
if (players != nil) {
friends = players;
NSLog(#"Inside: %#", friends); //Properly shows the array
}
}];
NSLog(#"Outside: %#", friends): //Doesn't work, shows nil
}
But friends is still nil/null afterwards. Am I doing something wrong? Is there any way to save players and use it in a UITableView? Thanks.
***EDIT***
So here's the solution I put together.
typedef void(^CallbackBlock)(id object);
+ (void) retrieveFriends: (CallbackBlock)callback {
GKLocalPlayer *lp = [GKLocalPlayer localPlayer];
if (lp.authenticated) {
[lp loadFriendsWithCompletionHandler:^(NSArray *friends, NSError *error) {
if (friends != nil) {
[self loadPlayerDataWithIdentifiers:friends callback:^(NSArray *playersInfo) {
if (callback) callback(playersInfo);
}];
}
}];
}
}
+ (void) loadPlayerDataWithIdentifiers: (NSArray *) identifiers callback:(CallbackBlock)callback {
[GKPlayer loadPlayersForIdentifiers:identifiers withCompletionHandler:^(NSArray *players, NSError *error) {
if (error != nil) {
// Handle the error.
}
if (players != nil) {
if (callback) callback(players);
}
}];
}
The only thing is, my UITableView is in another class so I tried doing this and making the two methods above public. info isn't printing out anything. Any ideas?
[GameCenterHelper retrieveFriends:^(NSArray *info) {
NSLog(#"Friends Info: %#", info);
}];
Use callback blocks.
typedef void(^CallbackBlock)(id object);
- (void)loadPlayerDataWithIdentifiers:(NSArray *)identifiers callback:(CallbackBlock)callback {
[GKPlayer loadPlayersForIdentifiers:identifiers withCompletionHandler:^(NSArray *players, NSError *error) {
if (error != nil) {
// Handle the error.
}
if (callback) callback(players);
}];
}
You imply in your question that this is for a table view. If so, you need to reload your table after the data has been loaded.
[self loadPlayerDataWithIdentifiers:identifiers callback:^(NSArray *players) {
self.players = players;
[self.tableView reloadData];
}];
Crimson Chris is correct.
The other option is use GCD to wait for the response to comeback.
Or change this to synchronous call as you want to get the results immediately.