I want to fetch friend list of login user without using "FBFriendPickerViewController". So I used Graph API to do so but its not giving me the list of friends. I can login successfully and can able to fetch login user's information as well. I have followed this link https://developers.facebook.com/docs/graph-api/reference/v2.0/user/friendlists.
I have tried this code of snippet till now
-(IBAction)loginWithFacebook:(id)sender {
if (FBSession.activeSession.state == FBSessionStateOpen || FBSession.activeSession.state ==FBSessionStateOpenTokenExtended) {
// Close the session and remove the access token from the cache
// The session state handler (in the app delegate) will be called automatically
[FBSession.activeSession closeAndClearTokenInformation];
}
else {
[FBSession openActiveSessionWithPublishPermissions:#[#"publish_actions",#"manage_friendlists",#"public_profile",#"user_friends"]
defaultAudience:FBSessionDefaultAudienceEveryone
allowLoginUI:YES
completionHandler:^(FBSession *session, FBSessionState status, NSError *error) {
[self sessionStateChanged:session state:status error:error];
}];
}
}
-(void)sessionStateChanged:(FBSession *)session state:(FBSessionState) state error:(NSError *)error
{
// If the session was opened successfully
if (!error && state == FBSessionStateOpen){
NSLog(#"Session opened");
// Show the user the logged-in UI
[[FBRequest requestForMe] startWithCompletionHandler:^(FBRequestConnection *connection, NSDictionary<FBGraphUser> *user, NSError *error) {
NSLog(#"%#",user);
NSLog(#"email::: %#",[user objectForKey:#"email"]);
}];
return;
}
if (state == FBSessionStateClosed || state == FBSessionStateClosedLoginFailed){
// If the session is closed
NSLog(#"Session closed");
}
// Handle errors
if (error){
NSLog(#"Error");
NSString *alertText;
NSString *alertTitle;
// If the error requires people using an app to make an action outside of the app in order to recover
if ([FBErrorUtility shouldNotifyUserForError:error] == YES){
alertTitle = #"Something went wrong";
alertText = [FBErrorUtility userMessageForError:error];
[self showMessage:alertText withTitle:alertTitle];
} else {
// If the user cancelled login, do nothing
if ([FBErrorUtility errorCategoryForError:error] == FBErrorCategoryUserCancelled) {
NSLog(#"User cancelled login");
// Handle session closures that happen outside of the app
} else if ([FBErrorUtility errorCategoryForError:error] == FBErrorCategoryAuthenticationReopenSession){
alertTitle = #"Session Error";
alertText = #"Your current session is no longer valid. Please log in again.";
[self showMessage:alertText withTitle:alertTitle];
} else {
//Get more error information from the error
NSDictionary *errorInformation = [[[error.userInfo objectForKey:#"com.facebook.sdk:ParsedJSONResponseKey"] objectForKey:#"body"] objectForKey:#"error"];
// Show the user an error message
alertTitle = #"Something went wrong";
alertText = [NSString stringWithFormat:#"Please retry. \n\n If the problem persists contact us and mention this error code: %#", [errorInformation objectForKey:#"message"]];
[self showMessage:alertText withTitle:alertTitle];
}
}
// Clear this token
[FBSession.activeSession closeAndClearTokenInformation];
}
}
Now after login I tried to fetch friend list for that I have written
- (IBAction)fetchFrinds:(id)sender {
[FBRequestConnection startWithGraphPath:#"/me/friendlists"
parameters:#{#"fields": #"id,name"}
HTTPMethod:#"GET"
completionHandler:^(
FBRequestConnection *connection,
id result,
NSError *error
) {
NSLog(#"%#",result);
}];
}
As per the Facebook Graph API 2.0 docs on Friendlists:
/{user-id}/friendlists
A person's 'friend lists' - these are groupings of friends such as "Acquaintances" or "Close Friends", or any others that may have been created. They do not refer to the list of friends that a person has, which is accessed instead through the /{user-id}/friends edge.
So, with your current request, you're getting the friend-lists rather than the list of friends.
For getting a list of friends, you need to refer to:
Facebook Graph API 2.0 docs on List of Friends
NOTE:
Facebook seems to have changed it's implementation.
You can no longer get the entire list of friends.
Now... the list will be limited to only those friends who also happen to use your app.
To quote Facebook Graph API 2.0 doc:
Permissions
A user access token with user_friends permission is required to view the current person's friends.
This will only return any friends who have used (via Facebook Login) the app making the request.
If by friendslist you mean a list of friends for the logged in user then the graph path is me/friends. Something like this works for me after you have opened an active FBSession with read permissions.
NSMutableDictionary *params = [NSMutableDictionary dictionaryWithObjectsAndKeys:#"id,name,picture",#"fields",nil];
[FBRequestConnection startWithGraphPath:#"me/friends"
parameters:params
HTTPMethod:#"GET"
completionHandler:^(FBRequestConnection *connection, id result, NSError *error) {
if(error == nil) {
FBGraphObject *response = (FBGraphObject*)result;
NSLog(#"Friends: %#",[response objectForKey:#"data"]);
}
}];
Note that the result of the FBRequestConnection request on success is an FBGraphObject with the required information returned for key 'data'.
You can open an active FBSession with read permission of just basic_info before making an FBRequestConnection for the list of friends.
Hope this helps
Related
I have successfully logged user to Facebook using Facebook Graph API and now i need to fetch the user's Facebook page list (page status is already changed to Published).
My code looks like :
(void)checkLoginWithFacebook {
// If the session state is any of the two "open" states when the button is clicked
if (FBSession.activeSession.state == FBSessionStateOpen
|| FBSession.activeSession.state == FBSessionStateOpenTokenExtended) {
// Close the session and remove the access token from the cache
// The session state handler (in the app delegate) will be called automatically
// If the session state is not any of the two "open" states when the button is clicked
[self getListOfPages];
}
else
{
// Open a session showing the user the login UI
// You must ALWAYS ask for basic_info permissions when opening a session
// This will bypass the ios6 integration since it does not allow for a session to be opened
// with publish only permissions!
FBSession* sess = [[FBSession alloc] initWithPermissions:[NSArray arrayWithObjects:#"publish_actions",nil]];
[FBSession setActiveSession:sess];
[sess openWithBehavior:(FBSessionLoginBehaviorForcingWebView) completionHandler:^(FBSession *session, FBSessionState state, NSError *error)
{
[[AppDelegate appDel] sessionStateChanged:session state:state error:error];
if (state == FBSessionStateClosed || state == FBSessionStateClosedLoginFailed)
{
NSLog(#"session closed");
return ;
}
[self getListOfPages];
}];
}
}
(void)getListOfPages {
[FBRequestConnection startWithGraphPath:#"/me/accounts"
completionHandler:^(
FBRequestConnection *connection,
id result,
NSError *error
) {
/* handle the result */
NSLog(#"pages result: %# ",result);
}];
}
Response:
pages result: {
data = ();
}
Please Advice. Thanks.
Have you requested the manage_pages permission from the respective user through the login dialog? The code you posted look ok IMHO, I think you see an empty result because of the missing permission.
Using SDK 3.22.0.
if (FBSession.activeSession.isOpen) {
[FBSession.activeSession
requestNewPublishPermissions:#[#"publish_actions"]
defaultAudience:FBSessionDefaultAudienceFriends
completionHandler:^(FBSession *session, NSError *error) {
NSLog(#"error = %#", error);
NSLog(#"session open = %d", session.isOpen);
NSLog(#"session.permissions = %#", session.permissions);
NSLog(#"session.accessTokenData.declinedPermissions = %#", session.accessTokenData.declinedPermissions);
}];
}
else {
[FBSession
openActiveSessionWithPublishPermissions:#[#"publish_actions"]
defaultAudience:FBSessionDefaultAudienceFriends
allowLoginUI:YES
completionHandler:^(FBSession *session, FBSessionState status, NSError *error) {
NSLog(#"error = %#", error);
NSLog(#"session open = %d", session.isOpen);
NSLog(#"status = %lu", status);
NSLog(#"session.permissions = %#", session.permissions);
NSLog(#"session.accessTokenData.declinedPermissions = %#", session.accessTokenData.declinedPermissions);
}];
}
I'm testing with a user that doesn't have yet publish permissions and never declined it either. On FB Apps Settings on this account, the App Visibility is set to "Friends" and publishing permissions are not even on the settings list as they are for other users/apps.
In both cases of the code, the FB app opens and returns to my app immediately, without asking permissions. Response of declinedPermissions is an array with publish_actions.
My expectation is that FB app will ask the user to approve publishing.
I got reports from multiple users that experienced the same issue - not being able to add publish permissions, but some are able to get the permission.
One thing to add is that I had the same issue before submitting the app for FB approval with users outside of the test group, but when app got approved it started working for those users. Now it seems like the problem persists even when the app is approved, just for random users.
Am I doing anything wrong with the way I'm asking for permissions?
Looks like this worked:
- (BOOL)hasWritePermissions {
if (!FBSession.activeSession.isOpen) return NO;
return [FBSession.activeSession.permissions indexOfObject:#[#"publish_actions"]] != NSNotFound;
}
- (void)requestWritePermissions:(void(^)(BOOL status, NSError *error))callback {
if (self.hasWritePermissions) {
callback(YES, nil);
return;
}
if (FBSession.activeSession.isOpen) {
[FBSession.activeSession
requestNewPublishPermissions:#[#"publish_actions"]
defaultAudience:FBSessionDefaultAudienceFriends
completionHandler:^(FBSession *session, NSError *error) {
NSLog(#"error = %#", error);
NSLog(#"session open = %d", session.isOpen);
NSLog(#"session.permissions = %#", session.permissions);
NSLog(#"session.accessTokenData.declinedPermissions = %#", session.accessTokenData.declinedPermissions);
if (self.hasWritePermissions) {
callback(YES, nil);
}
else {
callback(NO, error);
}
}];
}
else {
[FBSession
openActiveSessionWithPublishPermissions:#[#"publish_actions"]
defaultAudience:FBSessionDefaultAudienceFriends
allowLoginUI:YES
completionHandler:^(FBSession *session, FBSessionState status, NSError *error) {
NSLog(#"error = %#", error);
NSLog(#"session open = %d", session.isOpen);
NSLog(#"status = %u", status);
NSLog(#"session.permissions = %#", session.permissions);
NSLog(#"session.accessTokenData.declinedPermissions = %#", session.accessTokenData.declinedPermissions);
[self requestWritePermissions:callback]; // this time, with an open session
}];
}
}
If there's no session, I run openActiveSessionWithPublishPermissions and then run again requestNewPublishPermissions.
Issue is that openActiveSessionWithPublishPermissions was firing the callback without even going to Facebook app for more permissions (looks like FB bug, will report), but this approach seems to solve it.
Another issue I found is that session.permissions are not always reflecting the permissions on Facebook. The only way I found to ensure I have the latest permissions is to issue an API request:
[FBRequestConnection startWithGraphPath:#"/me/permissions" completionHandler:^(FBRequestConnection *connection, id result, NSError *error) {
and check the result.data array for granted/declined permissions.
I had a similar issue with this method. I ended up opening the session with a full set of permissions (read & write ) and it solved my problem.
-(void)requestPublishPermissionsWithCompletion:(FBHandlerComp)completion{
if (self.session.isOpen && [self.session.permissions containsObject:#"publish_actions"]){
//we have an open session and all neceassarry pemissions
completion(true,nil);
}else{
//something in missing, to account to all diffrent scenarios (missing persmission, expired tokens, changes in user sessting etc..), we reinisilise the session and request permissions
//for publish permissions we need to ask for the whole set( read& publish)
NSMutableArray *permissions = [NSMutableArray arrayWithArray:self.writePersmissions];
[permissions addObjectsFromArray:self.readPersmissions];
self.session = [[FBSession activeSession]initWithAppID:nil permissions:permissions defaultAudience:FBSessionDefaultAudienceFriends urlSchemeSuffix:nil tokenCacheStrategy:nil];
[self openFacebookSessionWithCompleteion:^(BOOL result, NSError *error) {
if (result) {
completion(true,nil);
}else{
if (LOGGING_IS_ON) DebugLog(#"could not get publish permissions- could not open session %#",error);
completion(false,nil);
}
}];
}
}
I am not able to get the permissions for user_birthday and user_hometown.
I tried it with this code before but it only asks for the public profile and ignores others.
[FBSession openActiveSessionWithReadPermissions:#[#"public_profile",#"user_birthday",#"user_hometown"]
allowLoginUI:YES completionHandler:^(FBSession *session, FBSessionState status, NSError *error) {
if (error) {
NBAppDelegate* appDel = (NBAppDelegate*)`[UIApplication sharedApplication].delegate;
[appDel sessionStateChanged:session state:status error:error];
}
if ([session isOpen]) {
[self loginWithFBToken:session name:sender];
}
}];
Then someone suggested to ask for additional permissions after getting the public profile, so i even tried that to no good.
Here is the code for that
- (void)loadFbDetails
{
NSArray *permissionsNeeded = #[#"user_hometown", #"user_birthday"];
// Request the permissions the user currently has
[FBRequestConnection startWithGraphPath:#"/me/permissions"
completionHandler:^(FBRequestConnection *connection, id result, NSError *error) {
if (!error){
// These are the current permissions the user has:
NSDictionary *currentPermissions= [(NSArray *)[result data] objectAtIndex:0];
// We will store here the missing permissions that we will have to request
NSMutableArray *requestPermissions = [[NSMutableArray alloc] initWithArray:#[]];
// Check if all the permissions we need are present in the user's current permissions
// If they are not present add them to the permissions to be requested
for (NSString *permission in permissionsNeeded){
if (![currentPermissions objectForKey:permission]){
[requestPermissions addObject:permission];
}
}
// If we have permissions to request
if ([requestPermissions count] > 0){
// Ask for the missing permissions
[FBSession.activeSession
requestNewReadPermissions:requestPermissions
completionHandler:^(FBSession *session, NSError *error) {
if (!error) {
// Permission granted
NSLog(#"new permissions %#", [FBSession.activeSession permissions]);
// We can request the user information
[self makeRequestForUserData];
} else {
// An error occurred, we need to handle the error
// See: https://developers.facebook.com/docs/ios/errors
}
}];
} else {
// Permissions are present
// We can request the user information
[self makeRequestForUserData];
}
} else {
// An error occurred, we need to handle the error
// See: https://developers.facebook.com/docs/ios/errors
}
}];
}
-(void)makeRequestForUserData
{
[FBRequestConnection startWithGraphPath:#"me?fields=birthday,hometown"
completionHandler:^(FBRequestConnection *connection, id result, NSError *error) {
if (!error) {
// Sucess! Include your code to handle the results here
NSLog(#"user events: %#", result);
} else {
NSLog(#"error: %#" , error);
}
}];
}
All it does is recursively go between my ios app and the fb native app, returning only with the public_profile in the permissions array.
Looks like i am missing something?
It should work if you're using an admin user of the app. Once you want to use the extended permissions with other users, you have to get your app reviewed by Facebook first.
See my answer here: facebook extended permission
Facebook does not allow apps to access that information by default. You have to ask permission to Facebook for you to be able to use that information. Add a video and instructions for a Facebook employee review how you're using birthday and hometown intel.
Ok, this is probably something very common, but I cannot find the solution anywhere. When using the Facebook SDK to handle logging in as a SSO, it works great in the iOS simulator. However, on the actual device, it fails in one very specific instance: If the iPhone/iPad is signed into Facebook via the Settings in iOS, it comes back with "Session Closed: User cancelled login" (this message is generated when the error received is of the FBErrorCategoryUserCancelled type). I have tried every instance of this:
Facebook iOS app installed and Signed In with Settings logged in [FAIL]
Facebook iOS app installed and not signed In with Settings logged in [FAIL]
Facebook iOS app not installed and Settings logged in [FAIL]
Facebook OS app Installed and signed In with Settings not logged in [SUCCESS]
Facebook iOS app Installed but not logged in and Settings not logged in [SUCCESS]
Facebook iOS app not installed and Settings no logged in [SUCCESS]
This all has to do with the Settings being logged in to Facebook. Now I know others have their apps able to have Facebook integration with Settings being signed in, so what am I missing? Here is my sign in code:
- (IBAction)loginWithFacebook:(id)sender {
if (FBSession.activeSession.state == FBSessionStateOpen || FBSession.activeSession.state == FBSessionStateOpenTokenExtended) {
[FBSession.activeSession closeAndClearTokenInformation];
}
[FBSession openActiveSessionWithReadPermissions:#[#"basic_info",#"public_profile",#"email"]
allowLoginUI:YES
completionHandler: ^(FBSession *session, FBSessionState state, NSError *error) {
// Retrieve the app delegate
AppDelegate* appDelegate = [UIApplication sharedApplication].delegate;
// Call the app delegate's sessionStateChanged:state:error method to handle session state changes
[appDelegate sessionStateChanged:session state:state error:error];
}];
}
Here is my code that handles all of this from the AppDelegate:
- (void)sessionStateChanged:(FBSession *)session state:(FBSessionState) state error:(NSError *)error {
// If the session was opened successfully
if (!error && state == FBSessionStateOpen){
NSLog(#"Session opened");
[[FBRequest requestForMe] startWithCompletionHandler:
^(FBRequestConnection *connection,
NSDictionary<FBGraphUser> *user,
NSError *error) {
if (!error) {
NSString *firstName = user.first_name;
NSString *lastName = user.last_name;
NSString *facebookId = user.id;
NSString *email = [user objectForKey:#"email"];
[Data facebookLogin:facebookId email:email firstName:firstName lastName:lastName];
}
}];
return;
}
if (state == FBSessionStateClosed || state == FBSessionStateClosedLoginFailed){
// If the session is closed
NSLog(#"Session closed");
}
// Handle errors
if (error){
NSLog(#"Error");
NSString *alertText;
NSString *alertTitle;
// If the error requires people using an app to make an action outside of the app in order to recover
if ([FBErrorUtility shouldNotifyUserForError:error] == YES){
alertTitle = #"Something went wrong";
alertText = [FBErrorUtility userMessageForError:error];
UIAlertView *alert = [[UIAlertView alloc] initWithTitle: #"Notice" message: alertText delegate: nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alert show];
} else {
// If the user cancelled login, do nothing
if ([FBErrorUtility errorCategoryForError:error] == FBErrorCategoryUserCancelled) {
// This is the part that gets called, right here!
NSLog(#"User cancelled login");
// Handle session closures that happen outside of the app
} else if ([FBErrorUtility errorCategoryForError:error] == FBErrorCategoryAuthenticationReopenSession){
alertTitle = #"Session Error";
alertText = #"Your current session is no longer valid. Please log in again.";
NSLog(#"%#",alertText);
// Here we will handle all other errors with a generic error message.
// We recommend you check our Handling Errors guide for more information
// https://developers.facebook.com/docs/ios/errors/
} else {
//Get more error information from the error
NSDictionary *errorInformation = [[[error.userInfo objectForKey:#"com.facebook.sdk:ParsedJSONResponseKey"] objectForKey:#"body"] objectForKey:#"error"];
// Show the user an error message
alertTitle = #"Something went wrong";
alertText = [NSString stringWithFormat:#"Please retry. \n\n If the problem persists contact us and mention this error code: %#", [errorInformation objectForKey:#"message"]];
NSLog(#"%#",alertText);
}
}
// Clear this token
[FBSession.activeSession closeAndClearTokenInformation];
// Show the user the logged-out UI
}
}
I understand this is a long post, but I figured it would be more beneficial to have all of the information available when talking about this.
I had a similar problem. I think what is happening is that the native iOS login machinery chokes on the #"public_profile" permission (or more likely the server response to it that it is getting from Facebook). To verify, try to remove this permission from your permissions array.
This is an annoying problem, since the Facebook docs state that this permission is now obligatory.
I'm using facebook-ios-sdk-3.10, Using this SDK I'll login and fetch user details from FB Its working fine for me.
This is the code I'm uisng
- (IBAction)fbLogin_click:(id)sender
{
if (AppDelegate.fbsession.state != FBSessionStateCreated) {
// Create a new, logged out session.
NSArray *permissions = [NSArray arrayWithObjects:#"offline_access", #"email", #"publish_stream", #"read_stream",#"read_friendlists",#"manage_friendlists",#"friends_about_me",#"publish_actions", nil];
// create a session object, with defaults accross the board, except that we provide a custom
// instance of FBSessionTokenCachingStrategy
AppDelegate.fbsession = [[FBSession alloc] initWithAppID:nil
permissions:permissions
urlSchemeSuffix:nil
tokenCacheStrategy:nil];
}
FBSessionLoginBehavior behavior = FBSessionLoginBehaviorForcingWebView;
if (AppDelegate.fbsession.state != FBSessionStateCreatedTokenLoaded) {
// even though we had a cached token, we need to login to make the session usable
[AppDelegate.fbsession openWithBehavior:behavior completionHandler:^(FBSession *session,
FBSessionState status,
NSError *error) {
if (error)
{
NSLog(#"Error");
}
[self GetFBUserDetails];
}];
}
}
-(void) GetFBUserDetails
{
if (AppDelegate.fbsession.isOpen)
{
[HUD show:YES];
// fetch profile info such as name, id, etc. for the open session
FBRequest *me = [[FBRequest alloc] initWithSession:AppDelegate.fbsession graphPath:#"me"];
self.pendingRequest= me;
[me startWithCompletionHandler:^(FBRequestConnection *connection,
NSDictionary<FBGraphUser> *user,
NSError *error) {
// because we have a cached copy of the connection, we can check
// to see if this is the connection we care about; a prematurely
// cancelled connection will short-circuit here
if (me != self.pendingRequest) {
return;
}
self.pendingRequest = nil;
// self.pendingLoginForSlot = -1;
// we interpret an error in the initial fetch as a reason to
// fail the user switch, and leave the application without an
// active user (similar to initial state)
if (error) {
return;
}
[AppDelegate.fbsession closeAndClearTokenInformation];
[FBSession.activeSession close];
[FBSession.activeSession closeAndClearTokenInformation];
FBSession.activeSession=nil;
[self FacebookCustomerRegister:user];
}];
}
}
In such case some user create Facebook account and not very his account through email, when he try to login via my app it shows empty screen after click login button, there is no action further. how can I notify the user "You not yet verify your FB account" and it not return to my app. how can I fetch the response from there ?
can anyone help me for this ?
The email address you get from Facebook using the Graph API or a FQL query is a verified email. If an account hasn't verified it's email yet it's not possible to get it.
so when you fetch user info and if you are not getting the email when you have the permission to get then user is no verified and you can show an alert or any info to user about verifying the email and information
check more detail here Is it possible to check if an email is confirmed on Facebook?