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.
Related
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
}];
I am trying to get all the events that user has created for that i am trying to request permission for accessing the user_events.
However thus far facebook is not asking user for permission to access events. I have checked the test user app permission profile and my app is only getting profile info but not events settings.
The facebook sdk i am using is 3.19
if ([FBSession.activeSession.permissions indexOfObject:#"user_events"]==NSNotFound) {
[FBSession.activeSession requestNewReadPermissions:#[#"user_events"] completionHandler:^(FBSession *session, NSError *error) {
if (!error) {
action();
}
else if (error.fberrorCategory != FBErrorCategoryUserCancelled)
{
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Permission denied"
message:#"Unable to get permission to post"
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alertView show];
}
}];
}
else
{
action();
}
user_events needs to get approved by Facebook before it works with any user, else it will only work for users with a role in your app: https://developers.facebook.com/docs/apps/review/login
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];
}
}];
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.
Recently I was assigned to survey how to use iOS framework ACAccount and Social to implement facebook post function. It is quite simple to gain access of the account configured in setting.
if(!_accountStore)
_accountStore = [[ACAccountStore alloc] init];
ACAccountType *facebookTypeAccount = [_accountStore accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierFacebook];
// Check if there is any faceboook account
NSArray *accounts = [_accountStore accountsWithAccountType:facebookTypeAccount];
if (![accounts count]) {
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:#"alert" message:#"No facebook account configured in setting." delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alert show];
return;
}
[_accountStore requestAccessToAccountsWithType:facebookTypeAccount
options:#{ACFacebookAppIdKey: #"FACEBOOK-APP-ID", ACFacebookPermissionsKey: #[#"email"]}
completion:^(BOOL granted, NSError *error) {
if(granted){
NSArray *accounts = [_accountStore accountsWithAccountType:facebookTypeAccount];
_facebookAccount = [accounts lastObject];
NSLog(#"Success");
}else{
// ouch
NSLog(#"Failed, Error: %#", error);
dispatch_async(dispatch_get_main_queue(), ^{
NSString *message = [NSString stringWithFormat:#"App access denied, please grant access in Setting->Facebook. Error message::%#", error];
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:#"Alert" message:message delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alert show];
});
}
}];
Once the app gain access to facebook, it can post message by using SLComposeViewController:
- (IBAction)postButtonPressed:(id)sender {
if (!_facebookAccount) {
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:#"alert" message:#"login first" delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alert show];
return;
}
SLComposeViewController *fbController = [SLComposeViewController composeViewControllerForServiceType:SLServiceTypeFacebook];
if([SLComposeViewController isAvailableForServiceType:SLServiceTypeFacebook]) {
SLComposeViewControllerCompletionHandler __block completionHandler=^(SLComposeViewControllerResult result){
[fbController dismissViewControllerAnimated:YES completion:nil];
switch(result){
case SLComposeViewControllerResultCancelled:
default:
{
NSLog(#"Cancelled.....");
}
break;
case SLComposeViewControllerResultDone:
{
NSLog(#"Posted....");
}
break;
}};
[fbController setInitialText:#"Test message"];
[fbController setCompletionHandler:completionHandler];
[self presentViewController:fbController animated:YES completion:nil];
} else {
NSLog(#"no facebook setup");
}
Here comes my question. I found that there is a method in ACAccountStore which is used to renew expired credential of an ACAccount:
- (void)renewCredentialsForAccount:(ACAccount *)account completion:(ACAccountStoreCredentialRenewalHandler)completionHandler;
But I don't even know how to check whether the credential is expired so that I can renew it. Anyone got an idea about this?
Oh, by the way, we just want to use the native Social framework to do simple work such as post some message or picture. So, if not needed, we are not going to use the facebook SDK.
If you know anything about how to check the credential is valid or not, please leave a comment or submit an answer, thank you:)
Updates 2013.11.20 11:10 AM
I learn something from my experiments to this issue..
One is not able to get certain type of accounts from account store before he gains access to them, so I should not check account count before request for access.
Renew notification called when the app using ACAccount is in background after facebook account changed. Currently, I only saw changes of access right triggers the notification.
If the user changes password, the system will pop out an alert when the user attempt to post something, which ask the user to change password.
I think monitor notifications of account change is enough to handle the changes. I'll accept the first answer.
You should renew the user acc everytime it is out of sync. This may happen if the user has changed his password or when the acc session has expired.
Yo can know you are in that scenario using the following notification:
ACAccountStoreDidChangeNotification
I don't think there is active way , best is to write renew function and call
renewCredentialsForAccount of the framework