Issue Logging in With Facebook on iOS App - ios

In my app, my main screen is a TableView which shows a list of posts from a Facebook Page. I access those posts using Open Graph, which means a user has to be logged in first. So, I have it pop up with a UIAlertView which tells the user this. Clicking ok presents the FBLoginButton, which I have like this:
FBSDKLoginButton *loginButton = [[FBSDKLoginButton alloc] init];
// Optional: Place the button in the center of your view.
loginButton.center = self.view.center;
[self.view addSubview:loginButton];
Clicking the Login Button takes me to Facebook, where I give it permission. It then goes back to the app, but shows an empty TableView with a Facebook Logout button in the middle. My viewWillAppear in the app is like this:
if ([FBSDKAccessToken currentAccessToken]) {
[[[FBSDKGraphRequest alloc] initWithGraphPath:#"/v2.3/PAGEID/feed?limit=20" parameters:[NSMutableDictionary dictionaryWithObject:#"id, message, full_picture, link" forKey:#"fields"]]
startWithCompletionHandler:^(FBSDKGraphRequestConnection *connection, id result, NSError *error) {
if (!error) {
//performs code to get posts sorted into array
}
else {
NSLog(#"%#", error);
}
}];
} else {
NSLog(#"Not Logged In");
UIAlertView *login = [[UIAlertView alloc] initWithTitle:#"Please Login" message:#"Stronger Marriages now requires a login to Facebook in order to access its latest posts and messages. Thank you!" delegate:self cancelButtonTitle:nil otherButtonTitles:#"Ok", nil];
[login show];
}
I had thought that once I have given it permission, it would go back to the app, run the viewWillAppear code, and see there was a currentAccessToken and perform the code, but this is not happening. If I now force close the app, it will show everything fine, with no logout button visible. How do I get it to do this once it redirects to app after logging in?

The easiest way to respond directly to a login via the FBSDKLoginButton is by making your ViewController implement the FBSDKLoginButtonDelegate protocol.
You can call the loginButton:didCompleteWithResult:error: function in this protocol to immediately respond to a login as soon as the user returns to your app from Facebook. It would look something like this:
- (void) loginButton:(FBSDKLoginButton *)loginButton
didCompleteWithResult:(FBSDKLoginManagerLoginResult *)result
error:(NSError *)error {
if ([result token]) {
// Your code here
} else {
NSLog(#"Not Logged In");
// Show error here
}

I'm not sure why viewWillAppear doesn't get called, but I have a different solution. When the Facebook token changes, the SDK sends out a notification. You can subscribe to this, perhaps in your viewDidLoad, and run your code in the callback.
[[NSNotificationCenter defaultCenter] addObserverForName:FBSDKAccessTokenDidChangeNotification
object:nil
queue:[NSOperationQueue currentQueue]
usingBlock:^(NSNotification *note) {
NSLog(#"received access token change notif");
// do your stuff here
}];

Related

FBFriendPickerViewController friend list is empty

I am trying to use FBFriendPickerViewController to get a list of facebook friends to send app invitations. The friend list is empty whenever I test the code. Please see the code below:
#implementation FacebookInviteViewController
- (void)showFriendPicker
{
FBFriendPickerViewController *friendPicker = [[FBFriendPickerViewController alloc] init];
friendPicker.title = #"Invite Friends";
friendPicker.delegate = self;
[friendPicker loadData];
AppController *appController = (AppController *)[[UIApplication sharedApplication] delegate];
[appController.navController presentViewController:friendPicker animated:YES completion:nil];
}
-(void)inviteFromFacebookSelected
{
if (!FBSession.activeSession.isOpen) {
[FBSession openActiveSessionWithReadPermissions:nil
allowLoginUI:YES
completionHandler:^(FBSession *session,
FBSessionState state,
NSError *error) {
if (error) {
UIAlertView *alertView =
[[UIAlertView alloc] initWithTitle:#"Error"
message:error.localizedDescription
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alertView show];
} else if (session.isOpen) {
[self showFriendPicker];
}
}];
} else {
[self showFriendPicker];
}
}
In the Settings class (subclassed from CCLayer) I call the following method when the facebook button is pressed
-(void)showFbConnect {
facebookInviteViewController = [[FacebookInviteViewController alloc] init];
[facebookInviteViewController inviteFromFacebookSelected];
}
How can I solve this issue?
Graph API version 2.0 and above will only return App using friends.That means you will only get list of those friends who have authorized your app. So if you have created a new app at facebook and if none of your friends from your friendlist have authorized your app than FBFriendPickerViewController will be empty.
Check the change log at official facebook docs
App Friends: The /me/friends endpoint no longer includes the full list
of a person's friends. Instead, it now returns the list of that
person's friends who are also using your app.
If you want to test you can authorize your app from one of your friends account and than that friend will be shown in FBFriendPickerViewController
Small addition to #Bhumit's answer. You can go to your app page on Facebook and in the roles section add test users. Facebook creates test users for you at the click of a button. You can then manage these users and their friends.
This should help in testing.

Parse - Sign Up and Login at the same time

Using Parse (iOS framework), I am able to sign up and login successfully using two API.
When user log in, it will cache the user and so accessing "currentUser" will return appropriate object. But sign up API is not caching.
Is there any way that sign up itself will cache the user and avoid separate log in functionality?
It should automatically log you in as well. For example, if in your completion block in signUpInBackground, you have a segue to your main screen and in your main screen, it is supposed to show information related to the user, then it will because [PFUser currentUser] is set to the user that registered.
here is an example
[user signUpInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if (error) {
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:[[error userInfo] objectForKey:#"error"] message:nil delegate:self cancelButtonTitle:nil otherButtonTitles:#"Ok", nil];
[alertView show];
// Bring the keyboard back up, because they'll probably need to change something.
[_usernameField becomeFirstResponder];
}
else{
// Success!
[self performSegueWithIdentifier:#"goToMain" sender:self];
}
}];

FBFriendPickerViewController showing and empty table on iOS. Place Picker running fine

I have this code, copied from FB samples (where it runs fine), but in my App is showing just a blank table for the Friend Picker. Place Picker is running fine showing a full table.
What´s wrong with Friend Picker?
In the AppDelegate:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch.
[FBFriendPickerViewController class];
[FBPlacePickerViewController class];
return YES;
}
- (BOOL)application:(UIApplication *)application
openURL:(NSURL *)url
sourceApplication:(NSString *)sourceApplication
annotation:(id)annotation {
return [FBAppCall handleOpenURL:url sourceApplication:sourceApplication];
}
In the View Controller:
- (IBAction)pickFriendsButtonClick:(id)sender {
// FBSample logic
// if the session is open, then load the data for our view controller
if (!FBSession.activeSession.isOpen) {
// if the session is closed, then we open it here, and establish a handler for state changes
[FBSession openActiveSessionWithReadPermissions:#[#"public_profile", #"user_friends"]
allowLoginUI:YES
completionHandler:^(FBSession *session,
FBSessionState state,
NSError *error) {
if (error) {
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Error"
message:error.localizedDescription
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alertView show];
} else if (session.isOpen) {
[self pickFriendsButtonClick:sender];
}
}];
return;
}
// Create friend picker, and get data loaded into it.
FBFriendPickerViewController *friendPickerController = [[FBFriendPickerViewController alloc] init];
friendPickerController.title = #"Pick Friends";
friendPickerController.delegate = self;
[friendPickerController loadData];
[friendPickerController presentModallyFromViewController:self animated:YES handler:nil];
}
- (IBAction)pickPlacesButtonClick:(id)sender{
// FBSample logic
// if the session is open, then load the data for our view controller
if (!FBSession.activeSession.isOpen) {
// if the session is closed, then we open it here, and establish a handler for state changes
[FBSession openActiveSessionWithReadPermissions:#[#"public_profile", #"user_friends"]
allowLoginUI:YES
completionHandler:^(FBSession *session,
FBSessionState state,
NSError *error) {
if (error) {
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Error"
message:error.localizedDescription
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alertView show];
} else if (session.isOpen) {
[self pickPlacesButtonClick:sender];
}
}];
return;
}
// Initialize the place picker
FBPlacePickerViewController *placePickerController = [[FBPlacePickerViewController alloc] init];
// Set the place picker title
placePickerController.title = #"Pick Place";
// Hard code current location to Menlo Park, CA
placePickerController.locationCoordinate = CLLocationCoordinate2DMake(37.453827, -122.182187);
// Configure the additional search parameters
placePickerController.radiusInMeters = 1000;
placePickerController.resultsLimit = 50;
placePickerController.searchText = #"restaurant";
placePickerController.delegate = self;
// Load the place data
[placePickerController loadData];
// Show the place picker modally
[placePickerController presentModallyFromViewController:self animated:YES handler:nil];
}
- (void)facebookViewControllerDoneWasPressed:(id)sender {
NSLog(#"Done pressed");
[self dismissViewControllerAnimated:YES completion:NULL];
}
- (void)facebookViewControllerCancelWasPressed:(id)sender {
NSLog(#"Cancel pressed");
[self dismissViewControllerAnimated:YES completion:NULL];
}
Project:
Made a brand new Project with just a Single View.
Added 2 buttons and connected them with the action.
Copy/Paste the code from the sample.
Add the Facebook Framework.
Edit the plist file to add the Facebook Application numbers.
My table (Place Picker) is full for restaurants.
My table (Friend Picker) is still empty. What am I doing wrong?
step 1: open Facebook session if not exist
if ( !FBSession.activeSession.isOpen ) {
[FBSession openActiveSessionWithPermissions:#[#"public_profile", #"email", #"user_friends" ]
allowLoginUI:YES
completionHandler:^(FBSession *session, FBSessionState state, NSError *error) {
[self sessionStateChanged:session state:state error:error];
}];
}
step 2: request to get friends list (include require fields)
FBRequest* friendsRequest = [FBRequest requestWithGraphPath:#"me/friends?fields=picture,name,username,location,first_name,last_name" parameters:nil HTTPMethod:#"GET"];
[friendsRequest startWithCompletionHandler: ^(FBRequestConnection *connection, NSDictionary* result, NSError *error) {
//store result into facebookFriendsArray
self.facebookFriendsArray = [result objectForKey:#"data"];
}];
step 3: reload data to tableView from facebookFriendsArray
A couple of things you need to consider that are not explained well by Facebook in the current docs.
The users appearing in the friend picker are only users that have installed the app themselves. This is newer, probably not applicable at the time of the original post.
You need the user_friends permission from the user to see any friends.
Some more here: Facebook iOS Select Friends Table Blank
According to the latest Facebook API (v 2.0) you can not pick friends list directly. By default you can access only three permissions that are email, public_profile and user_friends.
For any additional permission you are required to ask Facebook (you can do it from Facebook developer account where you have created app) and also read change log because again apps created on or after 30 April, 2014 can not access old APIs.
if you have app created before stated date then use old Facebook API (I would recommend 3.13.1) and deprecated function (openActiveSessionWithPermissions) to create FBSession.
I had same issue and took me long to figure out. let me know if you have any trouble.
Make sure the permissions array you are using on user sign up includes the user_friends permission. If you're copying code it's easy to miss.
NSArray *permissionsArray = #[ #"public_profile", #"user_friends"];
Reference the array on Sign Up:
[PFFacebookUtils logInWithPermissions:permissionsArray block:^(PFUser *user, NSError *error) {
...
];
Then do the following in FB developer dashboard:
Create at least 2 test users
Set them to friends with each other
And on your app:
Have each user log in and provide permissions for your app (FB will only show users who are friends AND using the app)
The second user should be able to see the first user in their friend list.

Unable to login on loading FBLoginView

I am using facebook SDK for the first time in iOS. I have tried to follow Facebook Documentations for logging in the user: Login Tutorial and Getting Started.
I am adding FBLoginView to new view controller on a button press to prompt user for login. Issue I am facing is that after FBLoginView is created, I am immediately taken to two FBLoginViewDelegate Methods: loginViewShowingLoggedOutUser: and loginView:handleError:
loginView:handleError: states error as FBErrorCategoryUserCancelled while I never cancelled any request! Modal popup for login appears for 2 seconds, then dismisses automatically with cancel status.
I am really unable to understand that why it is happening. I am testing it on simulator. I have all 3 keys required in .plist file for Facebook SDK integration. Why request is cancelled automatically? Please help me with this issue.. Thanks.
Code:
#import "FBLoginViewController.h"
#interface FBLoginViewController ()
#end
#implementation FBLoginViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
FBLoginView *loginview = [[FBLoginView alloc] initWithReadPermissions:[NSArray arrayWithObjects:#"basic_info",#"user_photos",#"friend_photos", nil]];
loginview.frame = CGRectOffset(loginview.frame, (self.view.center.x - (loginview.frame.size.width / 2)), 5);
loginview.delegate = self;
[self.view addSubview:loginview];
}
#pragma mark - FBLoginDelegate
-(void) loginViewFetchedUserInfo:(FBLoginView *)loginView user:(id<FBGraphUser>)user
{
NSLog(#"user info: %#",user);
}
-(void) loginViewShowingLoggedInUser:(FBLoginView *)loginView
{
NSLog(#"User logged in");
}
-(void) loginViewShowingLoggedOutUser:(FBLoginView *)loginView
{
NSLog(#"User logged out");//Logs on console
}
-(void) loginView:(FBLoginView *)loginView handleError:(NSError *)error
{
NSString *alertMessage, *alertTitle;
// If the user should perform an action outside of you app to recover,
// the SDK will provide a message for the user, you just need to surface it.
// This conveniently handles cases like Facebook password change or unverified Facebook accounts.
if ([FBErrorUtility shouldNotifyUserForError:error]) {
alertTitle = #"Facebook error";
alertMessage = [FBErrorUtility userMessageForError:error];
// This code will handle session closures that happen outside of the app
// You can take a look at our error handling guide to know more about it
// https://developers.facebook.com/docs/ios/errors
} else if ([FBErrorUtility errorCategoryForError:error] == FBErrorCategoryAuthenticationReopenSession) {
alertTitle = #"Session Error";
alertMessage = #"Your current session is no longer valid. Please log in again.";
// If the user has cancelled a login, we will do nothing.
// You can also choose to show the user a message if cancelling login will result in
// the user not being able to complete a task they had initiated in your app
// (like accessing FB-stored information or posting to Facebook)
} else if ([FBErrorUtility errorCategoryForError:error] == FBErrorCategoryUserCancelled) {
NSLog(#"user cancelled login");
// For simplicity, this sample handles other errors with a generic message
// You can checkout our error handling guide for more detailed information
// https://developers.facebook.com/docs/ios/errors
} else {
alertTitle = #"Something went wrong";
alertMessage = #"Please try again later.";
NSLog(#"Unexpected error:%#", error);
}
if (alertMessage) {
[[[UIAlertView alloc] initWithTitle:alertTitle
message:alertMessage
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil] show];
}
}
#end
Strange, but it worked for me. In viewDidLoad, I changed initialization of FBLoginView from:
FBLoginView *loginview = [[FBLoginView alloc] initWithReadPermissions:[NSArray arrayWithObjects:#"basic_info",#"user_photos",#"friend_photos", nil]];
to:
FBLoginView *loginview = [[FBLoginView alloc] init];
Initializing without permissions is bringing up the modal popup for login now.
The problem could have been that you had a nil as the last item in the list.

nil resultURL after successfully posting with FBWebDialogs (Facebook SDK 3.5)

I'm integrating Facebook on my application to share links of websites. I'm using the Feed Dialog to accomplish this and I'm following this tutorial:
https://developers.facebook.com/docs/howtos/feed-dialog-using-ios-sdk/.
I've managed to login and post to Facebook but I wanted to add a message when the post was successful. The tutorial has this built in, but every time I post, I see "User canceled story publishing." in the Log which is the message that is displayed when the user clicks on cancel. Besides I've confirmed with the debugger that the param resultURL received by the handler is always nil even on successful posts.
At first I though it was a configuration issue in my Facebook App, but I decided to make a test. I opened the RPSSample that comes with the framework, added a completion handler to the presentRequestsDialogModallyWithSession call in the clickInviteFriends method in the RPSFriendsViewController.m view controller and I was getting a nil resultURL on successful posts there too.
I'm I missing something?
I know the 3.5 SDK version is very new, but according to the documentation I should be getting a valid resultURL param after posting through a Facebook Web Dialog so I'm not sure if it's a bug or if I'm missing some callback or handler somewhere.
Just in case, this is my call to the Feed Web Dialog. It has minor changes compared to the one that comes in the tutorial (it's actually simpler)
- (void)publish: (EntityToShare *)entityToShare {
NSMutableDictionary *params =
[NSMutableDictionary dictionaryWithObjectsAndKeys:
entityToShare.link, #"link",
nil];
// Invoke the dialog
[FBWebDialogs presentFeedDialogModallyWithSession:nil
parameters:params
handler:
^(FBWebDialogResult result, NSURL *resultURL, NSError *error) {
if (error) {
// Error launching the dialog or publishing a story.
NSLog(#"Error publishing story.");
} else {
if (result == FBWebDialogResultDialogNotCompleted) {
// User clicked the "x" icon
NSLog(#"User canceled story publishing.");
} else {
// Handle the publish feed callback
NSDictionary *urlParams = [self parseURLParams:[resultURL query]];
if (![urlParams valueForKey:#"post_id"]) {
// User clicked the Cancel button
NSLog(#"User canceled story publishing.");
} else {
// User clicked the Share button
NSString *msg = [NSString stringWithFormat:
#"Posted story, id: %#",
[urlParams valueForKey:#"post_id"]];
NSLog(#"%#", msg);
// Show the result in an alert
[[[UIAlertView alloc] initWithTitle:#"Result"
message:msg
delegate:nil
cancelButtonTitle:#"OK!"
otherButtonTitles:nil]
show];
}
}
}
}];
}
We have a fix for this in place and will be pushed out soon.
Edited:
This has now been fixed in the SDK release 3.5.1
Check it out here: https://developers.facebook.com/ios/

Resources