I've been using the old Facebook SDK and saved the accessToken and expirationDate inside the NSUserDefaults. But now in the new Facebook SDK 3.2 there is a new class called FBAccessTokenData and I can take the accessToken from there by using:
[FBSession activeSession].accessTokenData.accessToken
[FBSession activeSession].accessTokenData.expirationDate
My problem is when I restart my app and click on Facebook button again, it goes to Facebook to ask for a new token although I used the app 3 minutes before, login into Facebook and got new token. So the token hasn't been saved.
I'm trying to save the new accessToken into [FBSession activeSession] but accessToken and expirationDate are readonly properties.
I've been reading the Facebook SDK documentation for few times now, searched stackOverflow and the web and still didn't got any clue how to do that.
This is my code:
- (void)sessionStateChanged:(FBSession *)session state:(FBSessionState) state error:(NSError *)error
{
switch (state)
{
case FBSessionStateOpen:
{
// This is how i'm trying to save the token, like the old Facebook SDK.
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:[FBSession activeSession].accessTokenData.accessToken forKey:#"FBAccessTokenKey"];
[defaults setObject:[FBSession activeSession].accessTokenData.expirationDate forKey:#"FBExpirationDateKey"];
[defaults synchronize];
NSLog(#"FB: Session is open!");
}
break;
case FBSessionStateClosed:
case FBSessionStateClosedLoginFailed:
{
NSLog(#"FB: Session is closed or login failed");
[self closeSession];
}
break;
default:
break;
}
[[NSNotificationCenter defaultCenter] postNotificationName:FBSessionStateChangedNotification
object:session];
if (error)
{
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Error"
message:error.localizedDescription
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alertView show];
}
}
- (void)openSession
{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[FBSession openActiveSessionWithReadPermissions:nil allowLoginUI:YES completionHandler:
^(FBSession *session, FBSessionState state, NSError *error)
{
[self sessionStateChanged:session state:state error:error];
}];
}
- (void)closeSession
{
[FBSession.activeSession closeAndClearTokenInformation];
}
Thanks in advance!
So I found the answer after searching more, If someone has problem with the new Facebook SDK 3.2 accessToken, look at this link. and you'll find what you are looking for.
Basically you create a class that handles the token saving and loading. Than you save it into local plist file - pretty easy and simple.
Enjoy! :)
Related
After I login with my facebook account everything works fine, except the token gets cleared automatically every time i start the app, so it asks my to login again, i use the following code in the didFinishLaunchingWithOptions to check active session:
if (FBSession.activeSession.state == FBSessionStateCreatedTokenLoaded) {
NSLog(#"Found a cached session");
// If there's one, just open the session silently, without showing the user the login UI
[FBSession openActiveSessionWithReadPermissions:#[#"public_profile", #"email", #"user_friends",#"publish_actions,publish_stream"]
allowLoginUI:NO
completionHandler:
^(FBSession *session, FBSessionState state, NSError *error) {
NSLog(#"session exp. = %#",session.accessTokenData.expirationDate);
[self sessionStateChanged:session state:state error:error delegate:_delegate];
if (error) {
NSLog(#"error: %#",error.localizedDescription);
}
[[NSUserDefaults standardUserDefaults]setObject:[Utils languageSelectedStringForKey:#"Logout"] forKey:#"fbState"];
[[NSUserDefaults standardUserDefaults]synchronize];
[self sessionStateChanged:session state:state error:error delegate:nil];
}];
// If there's no cached session, we will show a login button
} else {
[[NSUserDefaults standardUserDefaults]setObject:[Utils languageSelectedStringForKey:#"Login with Facebook"] forKey:#"fbState"];
[[NSUserDefaults standardUserDefaults]synchronize];
NSLog(#"no cached session -> show a login button");
// UIButton *loginButton = [self.customLoginViewController loginButton];
// [loginButton setTitle:#"Log in with Facebook" forState:UIControlStateNormal];
}
so the Found a cached session message is printed, but when i check the session i found it cleared, what i noticed is the method openActiveSessionWithReadPermissions not called!!!
Any Suggestions, please!!
I have found the problem, i was using wrong permissions which did not allow me to store the session, so i edited the permissions as below:
#[#"public_profile", #"email", #"user_friends",#"publish_actions"]
So after i removed publish_stream everything worked perfectly.
I have special requirement of client that he want to be able to login to Facebook from within the app itself, i mean he don't want app to open an external browser where user enters his details, he just want to go with native iOS view having username/mobile and password field.
I have already tried googling but could not find any solution so just want to know is it possible to do and if yes then how?
Thanks in advance.
Regards,
Mahesh.
Use following method :
-(void)openFacebookAuthentication
{
NSArray *permission = [NSArray arrayWithObjects:kFBEmailPermission,kFBUserPhotosPermission, nil];
FBSession *session = [[FBSession alloc] initWithPermissions:permission];
[FBSession setActiveSession: [[FBSession alloc] initWithPermissions:permission] ];
[[FBSession activeSession] openWithBehavior:FBSessionLoginBehaviorForcingWebView completionHandler:^(FBSession *session, FBSessionState status, NSError *error) {
switch (status) {
case FBSessionStateOpen:
[self getMyData];
break;
case FBSessionStateClosedLoginFailed: {
// prefer to keep decls near to their use
// unpack the error code and reason in order to compute cancel bool
NSString *errorCode = [[error userInfo] objectForKey:FBErrorLoginFailedOriginalErrorCode];
NSString *errorReason = [[error userInfo] objectForKey:FBErrorLoginFailedReason];
BOOL userDidCancel = !errorCode && (!errorReason || [errorReason isEqualToString:FBErrorLoginFailedReasonInlineCancelledValue]);
if(error.code == 2 && ![errorReason isEqualToString:kFBSdkUserLoginFail]) {
UIAlertView *errorMessage = [[UIAlertView alloc] initWithTitle:kFBAlertTitle
message:kFBAuthenticationErrorMessage
delegate:nil
cancelButtonTitle:kOk
otherButtonTitles:nil];
[errorMessage performSelectorOnMainThread:#selector(show) withObject:nil waitUntilDone:YES];
errorMessage = nil;
}
}
break;
// presently extension, log-out and invalidation are being implemented in the Facebook class
default:
break; // so we do nothing in response to those state transitions
}
}];
permission = nil;
}
Here is my Post : Login with facebook in iPhone without redirecting to the web browser?
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.
I am using the sample program to learn the facebook-ios-sdk, I found a problem: if I logged into Facebook on my device and run that sample app, everything is fine, however, when I delete my Facebook account from my device and run that sample app again, I can still pass the login process and SCViewController can still be seen (There is a fast-app-switch to Facebook app process, but only I need to do is to click the "OKey" button, I don't need to fill any email/password information to log into the Facebook).
I checked the code and found that after my account is removed from the device, the token in FBSession.activeSession.accessToken is still considered as valid. Is there any problem? and How can I clear the token and make the log in dialog pop up? I have already invoked [FBSession.activeSession closeAndClearTokenInformation] when log out, the token should be cleared, according to the Facebook sdk document, but it is not the case.
the environment I use: XCode 4.6.1, iPad 6.1 simulator and facebook-ios-sdk v3.2.1.
updated: paste some code here:
in SCAppDelegate.m, I added 3 functions, which is not in the sample code but in the SDK online document:
- (void)showLoginView
{
UIViewController* topViewController = [self.navigationController topViewController];
UIViewController* modalViewController = [topViewController modalViewController];
// If the login screen is not already displayed, display it. If the login screen is
// displayed, then getting back here means the login in progress did not successfully
// complete. In that case, notify the login view so it can update its UI appropriately.
if (![modalViewController isKindOfClass:[SCLoginViewController class]]) {
SCLoginViewController* loginViewController = [[SCLoginViewController alloc]
initWithNibName:#"SCLoginViewController"
bundle:nil];
[topViewController presentModalViewController:loginViewController animated:NO];
} else {
SCLoginViewController* loginViewController = (SCLoginViewController*)modalViewController;
[loginViewController loginFailed];
}
}
- (void)sessionStateChanged:(FBSession*)session state:(FBSessionState)state error:(NSError*)error
{
switch (state) {
case FBSessionStateOpen: {
UIViewController* topViewController = [self.navigationController topViewController];
if ([[topViewController modalViewController]isKindOfClass:[SCLoginViewController class]]) {
[topViewController dismissModalViewControllerAnimated:YES];
}
}
break;
case FBSessionStateClosed:
case FBSessionStateClosedLoginFailed:
[self.navigationController popToRootViewControllerAnimated:NO];
[FBSession.activeSession closeAndClearTokenInformation];
[self showLoginView];
break;
default:
break;
}
if (error) {
UIAlertView* alertView = [[UIAlertView alloc]initWithTitle:#"Error" message:error.localizedDescription delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alertView show];
}
}
- (void)openSession
{
[FBSession openActiveSessionWithReadPermissions:nil allowLoginUI:YES completionHandler:^(FBSession* session, FBSessionState status, NSError* error){
[self sessionStateChanged:session state:status error:error];}];
}
Then in SCLoginViewController.m, I add a button, instead of using the existing FBLoginView object, to do the login job. The handler function for that button is as follows:
- (IBAction)performLogin:(id)sender {
//[self.spinner startAnimating];
SCAppDelegate* appDelegate = [UIApplication sharedApplication].delegate;
[appDelegate openSession];
}
Then in SCViewController.m, I add another button to do the log out job. The handler function for that button is as follows:
- (IBAction)logoutButtonWasPressed:(id)sender {
[FBSession.activeSession closeAndClearTokenInformation];
}
other code is almost the same as that in the sample code.
Apart from the clearing the token, you also need to clear the cookie that is stored in Safari when you use it to log into Facebook.
The following works for me with Facebook SDK 3+ on iOS 5.1+:
[FBSession.activeSession closeAndClearTokenInformation];
NSHTTPCookieStorage *storage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
for(NSHTTPCookie *cookie in [storage cookies])
{
NSString *domainName = [cookie domain];
NSRange domainRange = [domainName rangeOfString:#"facebook"];
if(domainRange.length > 0)
{
[storage deleteCookie:cookie];
}
}
I've read http://developers.facebook.com/docs/howtos/link-to-your-native-app/ and I am confused on how I am supposed to handle deep linking in 3.0. Say the user clicks an appRequest for my app and FB opens my app with a special URL. I have my Appdelegate's openURL method do:
return [FBSession.activeSession handleOpenURL:url];
The tutorial says:
If your app requires an authorized user, handle the processing of the target URL in the
SDK callbacks implemented after a successful login, the fbDidLogin method.
However, the fbDidLogin delegate method is no longer called because in 3.0 we switch to using FBSession.activeSession instead of using a facebook.m object. In fact, none of the FBSessionDelegate methods will ever be called because the facebook object's state is never changed. So where should I process the URL?
You would likely handle this in the handler you've set up when opening a session.
Say for example that you opened the session using something like:
[FBSession openActiveSessionWithReadPermissions:nil
allowLoginUI:YES
completionHandler:^(FBSession *session,
FBSessionState state,
NSError *error) {
[self sessionStateChanged:session
state:state
error:error];
}];
You could put put the deep linking handling code in the method set to handle session changes that you can define, ex:
- (void)sessionStateChanged:(FBSession *)session
state:(FBSessionState) state
error:(NSError *)error
{
switch (state) {
case FBSessionStateOpen:
if (!error) {
// Handle deep link
}
break;
case FBSessionStateClosed:
self.user = nil;
break;
case FBSessionStateClosedLoginFailed:
self.user = nil;
[FBSession.activeSession closeAndClearTokenInformation];
break;
default:
break;
}
if (error) {
UIAlertView *alertView = [[UIAlertView alloc]
initWithTitle:#"Error"
message:error.localizedDescription
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alertView show];
}
}
To see a working sample for deep link handling, see https://github.com/fbsamples/ios-social-cafe/