Deep linking and FBSessionDelegate in Facebook iOS SDK 3.0 - ios

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/

Related

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.

iOS Facebook web share api - how to tell if the user has hit skip

I have a VC with a button hooked up to do a facebook share of some text and an image url.
I use the FB api which opens the FB webview - for the user to login. It then presents the share window with a "Skip" or "Share" button as shown...
I have in my app the following code to take care of the login and button actions ....
For some reason the "Skip" also posts as if the "OK" was hit. I tried cheking thr error code returned using the Error Utility but that does not work (app never gets into that section of code and "CANCELLED OUT" is never logged in NSLog, even when I hit the Skip button)
Any help with this will be most appreciated. Perhaps some way to recheck permissions?
(void)postStatusUpdate:(id)status
{
if (FBSession.activeSession.state != FBSessionStateCreatedTokenLoaded || [FBSession.activeSession.permissions
indexOfObject:#"publish_actions"] == NSNotFound) {
// Permission hasn't been granted, so ask for publish_actions
[FBSessionopenActiveSessionWithPublishPermissions:#[#"publish_actions"]
defaultAudience:FBSessionDefaultAudienceFriends
allowLoginUI:YES
completionHandler:^(FBSession *session, FBSessionState state,
NSError *error) {
[selfsessionStateChanged:session state:state error:error];
if([FBErrorUtilityerrorCategoryForError:error] ==
FBErrorCategoryUserCancelled)
{
NSLog(#"SNFacebookClient: CANCELLED OUT!!");
}
if (FBSession.activeSession.isOpen && !error) {
// Publish the story if permission was granted
[selfpublishStory:status];
}
}];
} else {
// If permissions present, publish the story
[selfpublishStory:status];
}
}
code here
I even tried in lldb to check the permissions array after "Skip" was pressed ...
po [FBSession activeSession].permissions
And find that even when skip is pressed the array contains the entry "publish_actions"
You need to check the session status, if the status is FBSessionStateOpen or FBSessionStateCreatedTokenLoaded, you can fetch user's data.
Otherwise, something wrong happen or user skipped it.
This approach is different of you, check my short comments to you understand the steps.
// Suppose my FBSession is stored in a property called session
#property (nonatomic, strong) FBSession *session;
// The session getter can be implemented like this
- (FBSession *)session
{
if(!_session)
{
_session = [[FBSession alloc] initWithPermissions:readPermissions];
[FBSession setActiveSession:_session];
}
return _session;
}
// Opening a new session
[self.session openWithCompletionHandler:^(FBSession *session, FBSessionState status, NSError *error) {
[self session:session stateChanged:status error:error];
}];
- (void)session:(FBSession *)session stateChanged:(FBSessionState)state error:(NSError *)error
{
switch(state)
{
case FBSessionStateOpen:
{
// Connection accepted
}
break;
case FBSessionStateCreatedTokenLoaded:
{
// Connection restored
}
break;
case FBSessionStateClosed:
// Connection closed
break;
case FBSessionStateClosedLoginFailed:
{
// Connection failed and closed
}
break;
case FBSessionStateCreated:
{
// Session created (not opened yet)
}
break;
case FBSessionStateCreatedOpening:
// Handler it if you need to do something while connection is opening
break;
case FBSessionStateOpenTokenExtended:
// Session token extended
break;
}
}

Can't save Facebook accessToken in SDK 3.2

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! :)

User cannot facebook log in again if they've deleted the facebook app in the past. Issue only arises from ios6.0 facebook integration [duplicate]

I'm using die Facebook SDK 3.1.1 to implement FB Connect in my iOS application. This works fine in the simple case with either the new FB integration (logged in on iOS) or falling back to the normal authorization via web view (I do not have the native Facebook application installed in both cases).
The problem occurs when I switch the account on iOS level. Logging out and logging in with a different FB user account.
To log in/authorize I perform:
[FBSession openActiveSessionWithReadPermissions:nil allowLoginUI:allowLoginUI
completionHandler:^(FBSession *session, FBSessionState state, NSError *error) {
[self sessionStateChanged:session state:state error:error];
}];
If then get a FBSessionStateClosedLoginFailed every time even though I perform a closeAndClearTokenInformation when that state is reached:
- (void)sessionStateChanged:(FBSession *)session
state:(FBSessionState) state
error:(NSError *)error
{
NSLog(#"Session State Changed: %u", [[FBSession activeSession] state]);
switch (state) {
case FBSessionStateOpen:
break;
case FBSessionStateClosed:
case FBSessionStateClosedLoginFailed:
NSLog(#"FBSessionStateClosedLoginFailed ERROR: %#", [error description]);
[[FBSession activeSession] closeAndClearTokenInformation];
break;
default:
break;
}
However, I receive the same state on every retry. My log says the following:
FBSDKLog: FBSession **INVALID** transition from FBSessionStateCreated to FBSessionStateClosed
FBSDKLog: FBSession transition from FBSessionStateCreated to FBSessionStateCreatedOpening
FBSDKLog: FBSession transition from FBSessionStateCreatedOpening to FBSessionStateClosedLoginFailed Session State Changed: 257
FBSessionStateClosedLoginFailed TOKEN: (null)
FBSessionStateClosedLoginFailed ERROR: Error Domain=com.facebook.sdk Code=2 "The operation couldn’t be completed. (com.facebook.sdk error 2.)" UserInfo=0xb24cc20 {com.facebook.sdk:ErrorLoginFailedReason=com.facebook.sdk:ErrorLoginFailedReason}
Can anyone reproduce this or has any idea where the problem might lie?
Another answer gives a way to manually resync the device with the server. I defined a method called fbRsync to call this code. Make sure to #import <Accounts/Accounts.h> in your implementation file and then define this method:
-(void)fbResync
{
ACAccountStore *accountStore;
ACAccountType *accountTypeFB;
if ((accountStore = [[ACAccountStore alloc] init]) && (accountTypeFB = [accountStore accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierFacebook] ) ){
NSArray *fbAccounts = [accountStore accountsWithAccountType:accountTypeFB];
id account;
if (fbAccounts && [fbAccounts count] > 0 && (account = [fbAccounts objectAtIndex:0])){
[accountStore renewCredentialsForAccount:account completion:^(ACAccountCredentialRenewResult renewResult, NSError *error) {
//we don't actually need to inspect renewResult or error.
if (error){
}
}];
}
}
I then call fbResync if openActiveSessionWithReadPermissions yields an error:
[FBSession openActiveSessionWithReadPermissions:permissions
allowLoginUI:YES
completionHandler:^(FBSession *session, FBSessionState state, NSError *error) {
if(error)
{
NSLog(#"Session error");
[self fbResync];
[NSThread sleepForTimeInterval:0.5]; //half a second
[FBSession openActiveSessionWithReadPermissions:permissions
allowLoginUI:YES
completionHandler:^(FBSession *session, FBSessionState state, NSError *error) {
[self sessionStateChanged:session state:state error:error];
}];
}
else
[self sessionStateChanged:session state:state error:error];
}];
The half a second delay is likely unnecessary, but it currently gives me piece of mind.
This seems to solve the problem for me. I can now switch between Facebook accounts and am able to log in. Yay!
I had the same problem. Check that your FB App is enabled in Settings -> Facebook.
Mine was disabled (even though I don't remember disabling it) and once I enabled it, it was fixed.
In my test process, I've added and removed my FB App several times from my FB Account, which is linked with my iPhone. It may explain why, magically, my app was disabled.
In ios 6 with fb sdk 3.1.1. Please pass permissions param as "nil or email" in "[FBSessio openActiveSessionWithReadPermissions..." method. Here my code it was works great.
#define IOS_NEWER_OR_EQUAL_TO_6 ( [ [ [ UIDevice currentDevice ] systemVersion ] floatValue ] >= 6.0 )
-(void)showFBLogin
{
[FBSession.activeSession closeAndClearTokenInformation];
NSArray *permissions = [NSArray arrayWithObjects:#"email, publish_actions, publish_stream", nil];
#ifdef IOS_NEWER_OR_EQUAL_TO_6
permissions = nil; or NSArray *permissions = [NSArray arrayWithObjects:#"email",nil];
#endif
NSLog(#"\npermissions = %#", permissions);
[FBSession openActiveSessionWithReadPermissions:permissions
allowLoginUI:YES
completionHandler:
^(FBSession *session,
FBSessionState state, NSError *error) {
NSLog(#"\nfb sdk error = %#", error);
switch (state) {
case FBSessionStateOpen:
[[FBRequest requestForMe] startWithCompletionHandler:
^(FBRequestConnection *connection, NSDictionary<FBGraphUser> *user, NSError *error) {
if (!error) {
//success
}
}];
break;
case FBSessionStateClosed:
//need to handle
break;
case FBSessionStateClosedLoginFailed:
//need to handle
break;
default:
break;
}
}];
}
I have the same problem on 3.1.3 FB SDK with iOS7.1. And I find a solution here. Hope it help!!
I have tried all answers here.
#loganathan nil permissions, #ill_always_be_a_warriors fbResync.
All of those don't work for me.
But I found it will works well when I launch the same code in iPhone 7.1 simulator
(without SSO"Single Sign On")
The same code works well on old version iOS FB SDK(not sure which version, but not 3.13)
(but no SSO will show when try to login)
So, I try to re-write a sample problem. I found several different here.
1. https://developers.facebook.com/docs/ios/getting-started Add new FacebookDisplayName
2. I modify my BundleID for iTune connect but I am not update it back to Faceboook App Dev
After modify those setting, it works on my iOS app with SSO feature.
Hope it help!!
Did you Try the RenewSystemCredential Methods? Take a look at this post:
Facebook iOS SDK extendAccessToken not Working - fbDidExtendToken not being called
I got the same error. But i used #ill_always_be_a_warriors method: -(void)fbResync but not work for me.
I finally found it's my permissions issue, i removed offline_access permission, it works, hope it helps some one.

Can I detect the *first* time a user has logged into an app using the facebook sdk?

Using the latest facebook sdk 3.1, I open a session requesting permissions using:
[FBSession openActiveSessionWithReadPermissions:permissions
allowLoginUI:allowLoginUI
completionHandler:^(FBSession *session, FBSessionState state, NSError *error) {
[self sessionStateChanged:session state:state error:error];
}];
My sessionStateChanged method looks like this:
- (void)sessionStateChanged:(FBSession *)session
state:(FBSessionState)state
error:(NSError *)error
{
switch (state) {
case FBSessionStateOpen:
// handle successful login
break;
case FBSessionStateClosed:
case FBSessionStateClosedLoginFailed:
// handle failed login
break;
default:
break;
}
}
Is there any way inside sessionStateChanged to distinguish if this is the first time the user has ever logged in (i.e. they have never before given us permission for this particular app)?
There is no way to know if this is the user's first time authorizing the application or not. Your best option is to track within your own application if they have logged in before.

Resources