How do I cancel a previous Facebook reauthorize call? - objective-c

A quick question about the Facebook SDK for iOS. I'm trying to implement the functionality for a user to grant my app access to extended permissions.
While the code works fine when the user accepts the extended permission request (from the Facebook iOS app the user gets redirected to), I'm having trouble detecting when the user has returned to the app while having neither accepted the permissions, or clicking cancel then returning to the app.
If I click 'Cancel' and multitask back into the app, there is nothing logged and nothing shown on-screen. When I try to re-authorize again, the Facebook SDK throws an exception:
FBSession: It is not valid to reauthorize while a previous reauthorize call has not yet completed.
While I can catch the exception, it still doesn't help as I can't figure out how to stop the previous call in order to allow the user to try to re-authorize again.
Here's the code I'm using at the moment:
#try {
[[FBSession activeSession] reauthorizeWithPermissions:[self requiredPermissions]
behavior:FBSessionLoginBehaviorWithFallbackToWebView
completionHandler:^(FBSession *session, NSError *error) {
if (!error) {
[self fetchUserPermissionsWithCompletionHandler:^(BOOL extendedGranted) {
if (extendedGranted) {
[self setCanPostToActivityStream:YES];
}
}];
}
else {
NSLog(#"%#", [error localizedDescription]);
}
}];
}
#catch (NSException *exception) {
NSLog(#"%#", exception);
}
Now, the issue is not with the code above - the code works fine. The issue I'm having, again, is cancelling the previous re-authorize call when the user doesn't return to the app successfully after allowing permissions. The Facebook SDK as it is doesn't seem to alert the app of this situation in any way.

From your App's delegate, when - (void)applicationDidBecomeActive:(UIApplication*)application is invoked, you need to make a call to the active session's handleDidBecomeActive method. Once you do that, the completion handler associated with your reauthorizeWithPublishPermissions call will be invoked, and will provide the appropriate error.
- (void)applicationDidBecomeActive:(UIApplication*)application
{
[[FBSession activeSession] handleDidBecomeActive];
}

What you should see coming back from the SDK is an error from the reauthorize completion handler.
{
"com.facebook.sdk:ErrorLoginFailedReason" = "com.facebook.sdk:ErrorReauthorizeFailedReasonUserCancelled";
}
I tested something similar with the following setup: Xcode 4.5, iOS 5, Authorize against m.facebook.com. After an initial authorization I put code to hit reauthorize, then clicked Cancel and got this error.
[FBSession.activeSession
reauthorizeWithPublishPermissions:
[NSArray arrayWithObject:#"publish_actions"]
defaultAudience:FBSessionDefaultAudienceFriends
completionHandler:^(FBSession *session, NSError *error) {
if (!error) {
// Success case
} else {
// Check the error, info
// [[error userInfo] objectForKey:#"com.facebook.sdk:ErrorLoginFailedReason"]
}
}];
If you are not seeing this then it is a bug and you may want to file it with Facebook.

Related

After update iOS 9 and Facebook sdk 4.6 the login window not open

I update my Xcode to 7 , and Facebook to 4.6 sdk.
this My warning :
Warning: Attempt to present <FBSDKContainerViewController: 0x159337700> on <UIAlertController: 0x159262700> whose view is not in the window hierarchy!
in My project the BitCode is NO - because if I turn it to Yes I got this Error :
ld:'/Users/MyName/Desktop/MyProjectName/ProjectName/ProjectName/Resources/Frameworks/Fabric.framework/Fabric(Fabric.o)' does not contain bitcode. You must rebuild it with bitcode enabled (Xcode setting ENABLE_BITCODE), obtain an updated library from the vendor, or disable bitcode for this target. for architecture arm64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
this is the parse method :
-(void)signInWithFacebookClicked
{
NSArray *permissions = [NSArray arrayWithObjects:#"email",#"user_friends", nil];
[PFFacebookUtils logInInBackgroundWithReadPermissions:permissions block:^(PFUser *user, NSError *error)
{
if (!user) // The user cancelled the Facebook login
{
NSLog(#"Uh oh. The user cancelled the Facebook login.");
}
else if (user.isNew) // New user (not stored on DB) - User signed up and logged in through Facebook
{
[self handleNewUser];
}
else if (user) // the user is exist at DB
{
// the user is exist at DB
}
else if (error)
{
// showAlertOfSomethingWentWrong
}
}];
}
this is FBSDKGraphRequest :
-(void)handleNewUser
{
FBSDKGraphRequest *request = [[FBSDKGraphRequest alloc] initWithGraphPath:#"me" parameters:#{#"fields": #"friends, first_name, gender, last_name, link, name, verified, picture, email"}];
[request startWithCompletionHandler:^(FBSDKGraphRequestConnection *connection, id result, NSError *error)
{
NSMutableDictionary *userData = (NSMutableDictionary *)result;
}];
my problem is that that line :
[PFFacebookUtils logInInBackgroundWithReadPermissions:permissions block:^(PFUser *user, NSError *error)
the run never go into this block in iPhone , in simulator this work fine.
I had a similar problem where I was trying to show a login alert on top of FBSDKContainerViewController.
In this call
- (void)logInWithReadPermissions:(NSArray *)permissions
fromViewController:(UIViewController *)fromViewController
handler:(FBSDKLoginManagerRequestTokenHandler)handler;
Facebook presents its own view controller and if you don't specify the fromViewController, "the topmost view controller will be automatically determined as best as possible."
In your case, it sounds like Facebook is trying to present on top of an alert that was dismissed, even if this is not the call being invoked.
do you add NSAppTransportSecurity & LSApplicationQueriesSchemes key to your info.plist?
I did find a workaround to this, thanks to #PastryPup's answer.
The warning is displayed when trying to present the Facebook login view controller on top of a dismissed alertview. Switching to a UIAlertController, however, fixed the problem.
My impression is that this works because UIAlertController is a full-fledged controller, and thus exists in the view hierarchy even after it has been dismissed.
The solution is to basically replace the UIAlertView with a UIAlertController, and call [PFFacebookUtils logInInBackgroundWithReadPermissions:permissions block:^(PFUser *user, NSError *error) inside the default action of the UIAlerController.
Link on how to implement a UIAlertController
I did some trick to make this work. I don't know if it's the best way to do it. But i just made a boolean that is false before the activity executes (the fb button is tapped). So after it returns, just set the boolean to true and have a condition in the ViewDidAppear that if the boolean is true perform the segue to the next window you want to go.
I hope it helps!

FBSDKLoginManager error code 308 and singleton does not resolve it

I am using Facebook iOS SDK 4.5.1 . Didn't want to use 4.6 as it force login flow to SafariVirwcontrollee only.
I always get error 308 when trying to login if FBSDKLoginManager was called before. Creating singleton as other threads suggested does not work
To summarize error 308 occured in two cases
1. Call login twice.
2. Logout and then login again.
Here is the code for singleton
static FBSDKLoginManager *sharedLoginManager = nil;
+(FBSDKLoginManager*)sharedLoginManager;
{
static dispatch_once_t oncePredecate;
dispatch_once(&oncePredecate,^{
sharedLoginManager=[[FBSDKLoginManager alloc] init];
});
return sharedLoginManager;
}
- (id)init {
if (self = [super init]) {
}
return self;
}
Code to login
if ([FBSDKAccessToken currentAccessToken])
{
[self processLoginInfo];
} else
{
[FBSDKSettings enableLoggingBehavior:FBSDKLoggingBehaviorNetworkRequests];
[[FacebookLoginSingleton sharedLoginManager] logOut];
[[FacebookLoginSingleton sharedLoginManager]
logInWithReadPermissions: #[#"public_profile",#"email",#"user_friends"]
handler:^(FBSDKLoginManagerLoginResult *result, NSError *error) {
if (error) {//}
}];
Code to logout
if ([FBSDKAccessToken currentAccessToken])
{
[[FacebookLoginSingleton sharedLoginManager] logOut];
[FBSDKAccessToken setCurrentAccessToken:nil];
}
I'm still dealing with this same error, and (assuming your info.plist, AppDelegate, and Facebook app are set up correctly), the issue completely goes away when not running the app with Xcode.
Yeah, it appears to be a bug where the only solution is to not use the debugging tools.
If you need to debug the login flow, there are probably a dozen different ways to do it outside if Xcode, but I had crashlytics installed and just let it log the crash enough times until I got out all the other kinks in my login code.

openActiveSessionWithPublishPermissions show login form twice

I'm trying to implement post wall using objective-c but when i do request new permission to 'publish_actions' with action session, the FB SDK login form is show again.
I'm looking for this tutorial: https://developers.facebook.com/docs/ios/publish-to-feed-ios-sdk/
In my application flow is basically this:
void (^completionHandler)(FBSession*, NSError*) = ^(FBSession*session, NSError*error){
if (session.isOpen && !error)
{
[FBWebDialogs presentFeedDialogModallyWithSession:session
parameters:params
handler:handler];
}
};
// abrie o dialog
BOOL isFBIntegration = [SLComposeViewController isAvailableForServiceType:SLServiceTypeFacebook];
if ([FBSession.activeSession.permissions indexOfObject:#"publish_actions"] == NSNotFound)
{
if(isFBIntegration)
{
[[FBSession activeSession] requestNewPublishPermissions:#[#"publish_actions"]
defaultAudience:FBSessionDefaultAudienceFriends
completionHandler:completionHandler];
}
else
{
[FBSession openActiveSessionWithPublishPermissions:#[#"publish_actions"]
defaultAudience:FBSessionDefaultAudienceEveryone
allowLoginUI:YES
completionHandler:^(FBSession *session, FBSessionState status, NSError *error) {
completionHandler(session, error);
}];
}
}
else
{
completionHandler(FBSession.activeSession, nil);
}
The user is already logged in because I do ask him to log when it enters the application:
HOME
[FBSession openActiveSessionWithReadPermissions:#[#"basic_info, user_friends, friends_interests"]
allowLoginUI:YES
completionHandler:nil];
When Facebook is native integration, all right, but when no, openActiveSessionWithPublishPermissions show login form again.
thank you.
Facebook iOS SDK 3.1 has now disabled openActiveSessionWithPublishPermissions. You will need to first establish a read only session, then additionally request write permissions. You can read more details about the upgrade process at iOS SDK Upgrade Guide.

Facebook login failing on iOS 6

When I try to login using Facebook iOS SDK I get the error The operation couldn't be completed (com.facebook.sdk error 2).
The state of the session is: FBSessionStateClosedLoginFailed.
THis is my code now:
-(void) callFBService{
NSArray *permissions = [[NSArray alloc] initWithObjects:#"email, publish_stream, user_likes, friends_likes", nil];
[FBSession openActiveSessionWithReadPermissions:permissions allowLoginUI:YES
completionHandler:^(FBSession *fbsession,
FBSessionState status,
NSError *error) {
if(error)
{
NSLog(#"Session error");
[self fbResync];
[NSThread sleepForTimeInterval:0.5]; //half a second
[FBSession openActiveSessionWithReadPermissions:permissions
allowLoginUI:YES
completionHandler:^(FBSession *fbsession, FBSessionState status, NSError *error) {
[self sessionStateChanged:fbsession state:status error:error];
}];
}
else
[self sessionStateChanged:fbsession state:status error:error];
}];
}
I have tried everything in the following posts:
The operation couldn’t be completed. (com.facebook.sdk error 2.) ios6
Facebook Registration : The operation couldn't be completed (com.facebook.sdk error 2)
Facebook SDK 3.1 iOS: Handle login if user remove app from Facebook Settings
Any ideas??? Please!
You're passing publish_stream in with read permissions, but publish_stream is a write permission. It's also deprecated (use publish_actions instead). Try removing that permission. You'll need to ask for that permission separately, after you get your user logged in with read permissions. See the SDK docs: https://developers.facebook.com/docs/technical-guides/iossdk/login/#read
In addition, a few things to check: Make sure your app on Facebook.com is configured correctly, including the bundle ID. Make sure the user you're attempting to log in has rights to the app (if the app is in sandbox mode, make sure the user is added as a tester and has approved this).

Handle invalid accessToken with FBSession openActiveSessionWithReadPermissions in Facebook iOS 3.1.1 SDK

Before anything, I have read both this and this questions to solve the problem below and before asking.
My problem is that when the accessToken gets expired (either because the expiration date passes, or manually by deleting the app from my Facebook's App Center) the following code:
if ([[FBSession activeSession] isOpen]) {
//do something
}
else {
[FBSession openActiveSessionWithReadPermissions:nil allowLoginUI:YES completionHandler:^(FBSession *session, FBSessionState status, NSError *error) {
if(FB_ISSESSIONOPENWITHSTATE(status)) {
//do something
}
}
}];
}
gets in the else block with FBSession.activeSession open but when the 'do something' is executed the accessToken is invalid so the request gets Error: HTTP status code: 400.
When I try to do the whole procedure twice immediately the FBSession asks for permission (either UIAlertView for iOS6 integrated facebook, Facebook App or Facebook website in Safari) and the rest runs smoothly.
My concern is why I have to do everything twice to work well and why Facebook SDK cannot detect in the first time that the activeSession and accessToken are invalid.
Thank you all in advance!
The questions you linked are relevant, especially Facebook SDK 3.1 - Error validating access token which explains the problem where the Facebook account on the device is out of sync with the server (I.e., if you deleted the app from App Center). As mentioned there, in 3.1.1 the SDK will call to renew the device token only when it gets the invalid response from the server. This is a trade off in convenience for less round-trips to the server.
Assuming your code block is executed on applicationDidFinishLaunching or something similar, it will go to the else block because the app starts with a new session. When it calls openActiveSessionWithReadPermissions, the iOS 6 device thinks the token is valid and will let the state go to Open, so then your "do something" gets executed. Only then does the SDK get the invalid response from the server and invalidate the device token. As a result, the next time the procedure is called, it will prompt the user appropriately to authorize again.
This is intentional. For now, you can consider a automatic retry in your application if the error code describes an invalid token. For example, see the Scrumptious sample postOpenGraph retry code. In your case, it may look closer to something like (I used requestForMe as the "do something" for demonstration purposes):
else {
[FBSessionopenActiveSessionWithReadPermissions:nil allowLoginUI:YES completionHandler:^(FBSession *session, FBSessionState status, NSError *error) {
if(FB_ISSESSIONOPENWITHSTATE(status)) {
//do something
[[FBRequest requestForMe] startWithCompletionHandler:^(FBRequestConnection *connection, id result, NSError *error) {
if (!error){
NSLog(#"success on first try");
} else if ([[error userInfo][FBErrorParsedJSONResponseKey][#"body"][#"error"][#"code"] compare:#190] == NSOrderedSame) {
//requestForMe failed due to error validating access token (code 190), so retry login
[FBSession openActiveSessionWithReadPermissions:nil allowLoginUI:YES completionHandler:^(FBSession *session, FBSessionState status, NSError *error) {
if (!error){
//do something again, or consider recursive call with a max retry count.
NSLog(#"success on retry");
}
}];
}
}];
}
}];
}