In the below design we need to connect to facebook from the app itself by entering the username and password in the app textfields. This is similar to the default facebook connect functionality by Apple in iOS6 and iOS7 Settings. So, please suggest how can I approach this design. Thank you guys. :)
http://i.stack.imgur.com/4tJr4.png
Facebook doesn't suggest to use your own views for getting facebook username and passwords for a user. The safest way is to let facebook sdk handle the login flow.
All you need to call is openActiveSessionWithReadPermissions: allowLoginUI: completionHandler: after you have successfully integrated the facebook iOS sdk. Publish permissions are always requested in the next step.
NSArray *permissions = [[NSArray alloc] initWithObjects:
#"email",nil];
_isUserAuthenticated = [FBSession openActiveSessionWithReadPermissions:permissions
allowLoginUI:allowUI
completionHandler:^(FBSession *session,
FBSessionState state,
NSError *error) {
if (error) {
NSLog(#"FACEBOOOK OPEN ACTIVE SESSION ERROR:%#",error);
//[self handleAuthError:error];
}
[self sessionStateChanged:session
state:state
error:error];
}];
_isUserAuthenticated is a bool for keeping track of the result.
Related
For a while now, my "Login with Facebook" button in my app doesn't show this dialog
anymore, but redirecting me to Facebook app. Is this normal? Is it the FacebookSDK or my login flow has a mistake? I followed the latest instructions at Facebook Login for IOS
I want to create my own "Login with Facebook" button but rather than redirecting to Facebook app, I want the above dialog shown instead. Please help me, I don't know the term to search in Google. Anyone can tell me or point me to a good tutorial? Thanks
This is due to Facebooks Graph API v2.x. You should read up on the changes here: https://developers.facebook.com/docs/apps/upgrading
1) You can downgrade to use Facebook SDK v3.13 or below.
OR
2) You can login with specified behavior FBSessionLoginBehaviorUseSystemAccountIfPresent.
Sample code:
FBSessionStateHandler completionHandler = ^(FBSession *session, FBSessionState state, NSError *error) {
[self sessionStateChanged:session state:state error:error];
};
if ([FBSession activeSession].state == FBSessionStateCreatedTokenLoaded) {
// we have a cached token, so open the session
[[FBSession activeSession] openWithBehavior:FBSessionLoginBehaviorUseSystemAccountIfPresent
completionHandler:completionHandler];
} else {
[FBSession.activeSession closeAndClearTokenInformation];
// create a new facebook session
FBSession *fbSession = [[FBSession alloc] initWithPermissions:#[#"email", #"public_profile"]];
[FBSession setActiveSession:fbSession];
[fbSession openWithBehavior:FBSessionLoginBehaviorUseSystemAccountIfPresent
completionHandler:completionHandler];
}
Reference: Native Facebook Login stopped working after SDK update to 3.14
I want to check whether permission is given to my app for facebook access in iOS settings so that I can use different flag with openWithBehaviour function of facebook for facebook social login.
1)
FBSessionLoginBehaviorWithNoFallbackToWebView i.e
[session openWithBehavior:FBSessionLoginBehaviorWithNoFallbackToWebView
completionHandler:^(FBSession *session,
FBSessionState status,
NSError *error) {
// Respond to session state changes,
// ex: updating the view
[_self sessionStateChanged:session state:status error:error];
}];
if permission is not given for my app in iOS settings for facebook and
2)
FBSessionLoginBehaviorUseSystemAccountIfPresent i.e
[session openWithBehavior:FBSessionLoginBehaviorUseSystemAccountIfPresent
completionHandler:^(FBSession *session,
FBSessionState status,
NSError *error) {
// Respond to session state changes,
// ex: updating the view
[_self sessionStateChanged:session state:status error:error];
}];
So is it possible to check whether my app is denied permission by the user in settings? Any help, suggestion is deeply appreciated.
(I want this because on iOS 6 devices if user has denied permission for my app in iOS facebook settings then facebook login fall back is interrupted).
Why don't you access [FBSession activeSession] before making the call? There is
#property (readonly, copy) NSArray *permissions;
and
#property (readonly) FBSessionState state;
to do checks before making any call for log in on the Facebook SDK. NSArray permissions contains NSStrings so you can easily detect what your session is allowed to do.
I solved this:
[accountStore requestAccessToAccountsWithType:facebookAccountType
options:options completion:^(BOOL granted, NSError *e) {
if (granted) {
NSlog(#"Permission granted");
// everything is fine i.e, facebook account is created in iOS device settings and permission is given to your app to use facebook information.
//call facebook login using openWithReadPermissions function of facebook SDK or openWithBehaviour with FBSessionLoginBehaviorWithUseSystemAccountIfPresent
}
else
{
NSLog(#"Permission rejected");
NSLog(#"Facebook error details: %#",e);
if ([e code] == ACErrorAccountNotFound) {
//This means user has not created facebook account in ios settings.
}
else if ([e code]== 7 || e==nil){
//This means user has denied permission for your app in iOS device facebook settings. So if user tries for social login using facebook then you can call openWithBehaviour facebook SDK function with FBSessionLoginBehaviorWithNoFallbackToWebView to manually give fall facebook fallback.
}
}
}];
So when I am logged in to ios's facebook integration, my code works great. I get an active access token with permissions to read and write, and I have actually written to people's walls and whatnot from the app. However, when my app uses safari to authenticate people's login credentials, there is the common error: "An active access token must be used to query information about the current user."
What I don't understand is why I can only get my access code from the ios facebook integration. Anyways, my relevant code below is implemented by the current view controller when it loads:
if (![FBSession activeSession].isOpen) {
[self openSession];
}
my method openSession is defined as follows
- (void)openSession
{
//create the permissions array
NSArray *permissions =
[NSArray arrayWithObjects:#"email", #"basic_info", nil];
[FBSession openActiveSessionWithReadPermissions: permissions
allowLoginUI:YES
completionHandler: ^(FBSession *session, FBSessionState status, NSError *error) {
NSLog(#"\n\nopenSession called error = %#\n\n\n",error);
}];
}
I then go on to continue in the viewDidLoadMethod
[FBRequestConnection
startForMeWithCompletionHandler:^(FBRequestConnection *connection,
id<FBGraphUser> user,
NSError *error)
{
NSLog(#"error requestion connection: %#",error);
}];
I look forward to the response. Thanks in advance.
Using Facebook SDK.
NSString *fbAccessToken = [[[FBSession activeSession] accessTokenData] accessToken];
If you prefer dot syntax,
NSString *fbAccessToken = [FBSession activeSession].accessTokenData.accessToken;`
I hope , it will work for u
It seems to me, thath at the very first session opening request, the iOS integrated Facebook, and the old "app switching" authorization works in a different way.
The first needs to open the session with read permissions only, then ask for publish permission at publish time.
The old one needs to request for every permission at the first time, so app will be able to post later on (otherwise not).
So I split the session opening logic in my facebook connect method:
-(void)connectWithSuccess:(EPPZSuccessBlock) successBlock
fail:(EPPZFailBlock) failBlock
{
if (FBSession.activeSession.isOpen)
{
if (successBlock) successBlock();
[self socialServiceDidConnect:self];
}
else
{
//This is what I need to decide somehow.
BOOL userHaveIntegrataedFacebookAccountSetup = NO;
if (userHaveIntegrataedFacebookAccountSetup)
{
//Request for a session with read permissions only, otherwise iOS integrated Facebook will throw me an exception.
[FBSession openActiveSessionWithReadPermissions:[NSArray arrayWithObject:#"user_about_me"]
allowLoginUI:YES
completionHandler:^(FBSession *session, FBSessionState status, NSError *error)
{ [self handleOpenSessionResponseWithSession:session status:status error:error success:successBlock fail:failBlock]; }];
}
else
{
//Request for session with every (incuding publish) permissions, otherwise non integrated Facebook won't let the app to post later.
[FBSession openActiveSessionWithPublishPermissions:self.publishPermissions
defaultAudience:FBSessionDefaultAudienceEveryone
allowLoginUI:YES
completionHandler:^(FBSession *session, FBSessionState status, NSError *error)
{ [self handleOpenSessionResponseWithSession:session status:status error:error success:successBlock fail:failBlock]; }];
}
}
}
But I need some kind of easy detection of which one to use, so the question goes: How to detect if user have an iOS integrated Facebook account setup before request session?
As far as I know, the proper way to find this out is to use
[SLComposeViewController isAvailableForServiceType:SLServiceTypeFacebook]
Note that this is iOS 6 and later only! Part of Social.framework.
Just as anton said.
//Facebook setup on users device.
BOOL haveIntegratedFacebookAtAll = ([SLComposeViewController class] != nil);
BOOL userHaveIntegratedFacebookAccountSetup = haveIntegratedFacebookAtAll && ([SLComposeViewController isAvailableForServiceType:SLServiceTypeFacebook]);
Using iOS 6 with the FacebookSDK splits the requests for read and publish permissions into two separate calls. I'm not sure why there's any benefit to this, but it seems to require presenting the user with the Facebook UI twice the first time thru.
In my app, I don't request anything from Facebook until a user chooses to use Facebook, in which case they are first presented with the UI to get read permissions, then again to get publish permissions. Control switches from my app to facebook (for read) back to my app and then immediately back to facebook (for publish) then back to my app.
This is an awful user experience, especially since the facebook screen with the "Okay" looks the same to the user. (Why am I pressing Okay twice?)
My code, in a nutshell is:
Check for a valid FBSession.activeSession
if not open call FBSession openActiveSessionWithReadPermissions
if successful call FBSession.activeSession reauthorizeWithPublishPermissions
then publish post
The code works, but the user experience is lousy. Am I missing something?
My understanding is that iOS 6 is requiring the double login for their ACAccountStore support, so the Facebook login tutorial implies that you should do this for all cases. Switching the app twice is a bad user experience and I think I have come up with a work around.
Firstly, for older iOS's (e.g iOS 5.0) can you just use openActiveSessionWithPublishPermissions: and do the read and publish permissions in one swoop. Secondly, this same call works if the user has never logged into Facebook from the Device Settings. Therefore, the following code seems to work like this:
If user has logged into Facebook from Device Settings: One dialog for
read and one dialog for publish.
Else if user has Facebook app installed:
switch to FB app once, and get 2 prompts in a row.
Otherwise: switch
to Safari once, and get 2 prompts in a row
I tested this code on an iOS6 and iOS5 device, using Facebook SDK 3.2.1
- (BOOL)hasFacebookInDeviceSettings
{
ACAccountStore *accountStore = [[ACAccountStore alloc] init];
ACAccountType *accountTypeFB = [accountStore accountTypeWithAccountTypeIdentifier:#"com.apple.facebook"];
BOOL hasFacebookBuiltinAccount = (accountTypeFB != nil);
return hasFacebookBuiltinAccount;
}
- (BOOL)hasLoggedInToFacebookInDeviceSettings
{
if (![self hasFacebookInDeviceSettings]) {
return NO;
}
BOOL result = [SLComposeViewController isAvailableForServiceType:SLServiceTypeFacebook];
return result;
}
- (void)openFacebookSessionWithAllowLoginUI:(BOOL)allowLoginUI
{
if (![self hasLoggedInToFacebookInDeviceSettings]) {
// Simpler if we don't have the built in account
[FBSession openActiveSessionWithPublishPermissions:#[#"publish_actions"]
defaultAudience:FBSessionDefaultAudienceFriends
allowLoginUI:allowLoginUI
completionHandler:^(FBSession *session,
FBSessionState state,
NSError *error) {
[self facebookSessionStateChanged:session
state:state
error:error];
}];
}
else if (!FBSession.activeSession.isOpen) {
__block BOOL recursion = NO;
[FBSession openActiveSessionWithReadPermissions:nil
allowLoginUI:allowLoginUI
completionHandler:^(FBSession *session,
FBSessionState state,
NSError *error) {
if (recursion) {
return;
}
recursion = YES;
if (error || !FBSession.activeSession.isOpen) {
[self facebookSessionStateChanged:session
state:state
error:error];
}
else {
assert(FBSession.activeSession.isOpen);
if ([FBSession.activeSession.permissions indexOfObject:#"publish_actions"] == NSNotFound) {
[FBSession.activeSession requestNewPublishPermissions:#[#"publish_actions"]
defaultAudience:FBSessionDefaultAudienceFriends
completionHandler:^(FBSession *session,
NSError *error) {
[self facebookSessionStateChanged:session
state:FBSession.activeSession.state
error:error];
}];
}
}
}];
}
}
hasFacebookInDeviceSettings tells you if this device even supports Facebook from the settings (i.e. this is iOS6+).
hasLoggedInToFacebookInDeviceSettings tells you if the user has signed into to Facebook from the iOS6 Facebook device settings.
You'll need to create your own facebookSessionStateChanged: and other code, as described in the login tutorial