Facebook SDK on iOS App Session Fixing - ios

I'm trying to authenticate users through facebook. I've read the SDK and done every necessary things that need to be done, BUT if I try to Login into my app while LoggedIn in this place,
It Tells me SESSION CLOSED but if I logged out and try to SIgnIn into my app, it checks if I've the facebook app and try to check if logged in if I'm, it updates and do what I want it to do, but if I'm not, it brings a Dialog for me to login. But, once I sign into the Image above, the other thing happens CLOSED SESSION ERROR. Would appreciate the help, thanks.
My Code Below
//For the button
- (IBAction)facebook:(id)sender {
// If the session state is any of the two "open" states when the button is clicked
if (FBSession.activeSession.state == FBSessionStateOpen || FBSession.activeSession.state == FBSessionStateOpenTokenExtended) {
[FBSession.activeSession closeAndClearTokenInformation];
} else {
[FBSession openActiveSessionWithReadPermissions:#[#"public_profile",#"user_friends"]
allowLoginUI:YES
completionHandler: ^(FBSession *session, FBSessionState state, NSError *error) {
// Retrieve the app delegate
appDelegate = [UIApplication sharedApplication].delegate;
// Call the app delegate's sessionStateChanged:state:error method to handle session state changes
[appDelegate sessionStateChanged:session state:state error:error];
if (state == FBSessionStateOpen) {
[self fetchUserDetails];
UINavigationController *vc = [self.storyboard instantiateViewControllerWithIdentifier:#"MainViewController"];
[self presentViewController:vc animated:YES completion:nil];
}
}];
}
}
//AppDelegate
- (BOOL)application:(UIApplication *)application
openURL:(NSURL *)url
sourceApplication:(NSString *)sourceApplication
annotation:(id)annotation
{
//return [FBAppCall handleOpenURL:url sourceApplication:annotation];
return [FBSession.activeSession handleOpenURL:url];
}
- (void)applicationDidBecomeActive:(UIApplication *)application
{
// Handle the user leaving the app while the Facebook login dialog is being shown
// For example: when the user presses the iOS "home" button while the login dialog is active
[FBAppCall handleDidBecomeActive];
}
// This method will handle ALL the session state changes in the app
- (void)sessionStateChanged:(FBSession *)session state:(FBSessionState) state error:(NSError *)error
{
// If the session was opened successfully
if (!error && state == FBSessionStateOpen){
NSLog(#"Session opened");
// Show the user the logged-in UI
return;
}
if (state == FBSessionStateClosed || state == FBSessionStateClosedLoginFailed){
// If the session is closed
NSLog(#"Session closed");
// Show the user the logged-out UI
}
// Handle errors
if (error){
NSLog(#"Error");
NSString *alertText;
NSString *alertTitle;
// If the error requires people using an app to make an action outside of the app in order to recover
if ([FBErrorUtility shouldNotifyUserForError:error] == YES){
alertTitle = #"Something went wrong";
alertText = [FBErrorUtility userMessageForError:error];
//[self showMessage:alertText withTitle:alertTitle];
NSLog(#"%#", alertText);
} else {
// If the user cancelled login, do nothing
if ([FBErrorUtility errorCategoryForError:error] == FBErrorCategoryUserCancelled) {
NSLog(#"User cancelled login");
// Handle session closures that happen outside of the app
} else if ([FBErrorUtility errorCategoryForError:error] == FBErrorCategoryAuthenticationReopenSession){
alertTitle = #"Session Error";
alertText = #"Your current session is no longer valid. Please log in again.";
//[self showMessage:alertText withTitle:alertTitle];
NSLog(#"%#", alertText);
// Here we will handle all other errors with a generic error message.
// We recommend you check our Handling Errors guide for more information
// https://developers.facebook.com/docs/ios/errors/
} else {
//Get more error information from the error
NSDictionary *errorInformation = [[[error.userInfo objectForKey:#"com.facebook.sdk:ParsedJSONResponseKey"] objectForKey:#"body"] objectForKey:#"error"];
// Show the user an error message
alertTitle = #"Something went wrong";
alertText = [NSString stringWithFormat:#"Please retry. \n\n If the problem persists contact us and mention this error code: %#", [errorInformation objectForKey:#"message"]];
//[self showMessage:alertText withTitle:alertTitle];
NSLog(#"%#", alertText);
}
}
// Clear this token
[FBSession.activeSession closeAndClearTokenInformation];
// Show the user the logged-out UI
}
}

I add a similar problem, in my case I found the reason, I was using an application id that was not correct (because of test environment/production environment configuration of my app, I was developing and I had no facebook app for the development application id).
I'm sharing my approach for debugging the problem:
just launch the app from xcode, put a break point where the error is intercepted (CordovaFacebook.m) and there if you are lucky you should see a better description of the problem
Furthermore, remember to set the correct fb app id inside .plist
remember also that if you are using a test application on facebook, that application should be either publicly available or you should set some test users inside the ROLES tab of the admin section
Another source of interesting debugging informations is the following:
- (BOOL)application:(UIApplication *)application
openURL:(NSURL *)url
sourceApplication:(NSString *)sourceApplication
annotation:(id)annotation
{
if (!url) {
return NO;
}
set a break point there and inspect the url variable content.

Related

FBSDKSharingDelegate callback not working, even when post works fine

FBSDKSharingDelegate callback is not working. I can post to facebook fine with the ios SDK, but I'd like to detect if the post was successful and take additional action to notify the user. However, the callback is not working for me. The delegate methods are not being called and I don't know why.
Using ios8, Parse as my backend. In Parse, the user is linked to FB. I'm using the IOS simulator.
What I've tried:
I've ensured that publish permissions are granted, saved, and linked to the Parse user. I've run a check and "publish_actions" are detected OK. The posting works fine as I can see the post on the facebook account. It's just the callback that is not working. I've checked my fb setup and it looks fine. For good measure at the very bottom I've included that relevant code from my app delegate. I've blocked out confidential keys with XXXX.
Code:
1st: See if user is logged in to Parse, if not, send to sign in and link to facebook account. Once that is done, I request "publish" permissions and link that additional permission to the Parse user. I know this works b/c when I recompile, it remembers the "publish" permissions and goes right to into the post.
#interface FacebookAPIPost () <FBSDKSharingDelegate>
#end
#implementation FacebookAPIPost
-(void)shareSegmentFacebookAPI { //if statement below
//1) logged in?, if not send to sign up screen
//2) else if logged in, link account to facebook account, then send post
//3) else send to post b/c signed up and linked already.
PFUser *currentUser = [PFUser currentUser];
if(!currentUser) {
[self pushToSignIn];
} else if(![PFFacebookUtils isLinkedWithUser:currentUser]){
[self linkUserToFacebook:currentUser];
NSLog(#"user account not linked to facebook");
} else {
[self shareSegmentWithFacebookComposer];
}
}
-(void)linkUserToFacebook:currentUser{
[PFFacebookUtils linkUserInBackground:currentUser withPublishPermissions:#[#"publish_actions"] block:^(BOOL succeeded, NSError *error) {
if(error){
NSLog(#"There was an issue linking your facebook account. Please try again.");
}
else {
NSLog(#"facebook account is linked");
//Send the facebook status update
[self shareSegmentWithFacebookComposer];
}
}];
}
-(void)shareSegmentWithFacebookComposer{
if ([[FBSDKAccessToken currentAccessToken] hasGranted:#"publish_actions"]) {
[self publishFBPost]; //publish
} else {
NSLog(#"no publish permissions"); // no publish permissions so get them, then post
[PFFacebookUtils linkUserInBackground:[PFUser currentUser]
withPublishPermissions:#[ #"publish_actions"]
block:^(BOOL succeeded, NSError *error) {
if (succeeded) {
NSLog(#"User now has read and publish permissions!");
[self publishFBPost];
}
}];
Here is where the post gets made:
-(void) publishFBPost{
FBSDKShareLinkContent *content = [FBSDKShareLinkContent new];
content.contentURL = [NSURL URLWithString:[self.selectedSegment valueForKey:#"linkToContent"]];
content.contentTitle = [self.selectedProgram valueForKey:#"programTitle"];
content.contentDescription = [self.selectedSegment valueForKey:#"purposeSummary"];
PFFile *theImage = [self.selectedSegment valueForKey:#"segmentImage"];
NSString *urlString = theImage.url;
NSURL *url = [NSURL URLWithString:urlString];
content.imageURL = url;
FBSDKShareDialog *shareDialog = [FBSDKShareDialog new];
[shareDialog setMode:FBSDKShareDialogModeAutomatic];
// [FBSDKShareDialog showFromViewController:self.messageTableViewController withContent:content delegate:self];
[shareDialog setShareContent:content];
[shareDialog setDelegate:self];
[shareDialog setFromViewController:self.messageTableViewController];
[shareDialog show];
}
Delegate methods below are not working. Meaning after the post is complete, I can see it on the FB account, but none of these delegate methods execute.
#pragma mark - delegate methods
- (void)sharer:(id<FBSDKSharing>)sharer didCompleteWithResults:(NSDictionary *)results {
// if ([sharer isEqual:self.shareDialog]) {
NSLog(#"I'm going to go crazy if this doesn't work.%#",results);
// Your delegate code
// }
}
- (void)sharer:(id<FBSDKSharing>)sharer didFailWithError:(NSError *)error
{
NSLog(#"sharing error:%#", error);
NSString *message = error.userInfo[FBSDKErrorLocalizedDescriptionKey] ?:
#"There was a problem sharing, please try again later.";
NSString *title = error.userInfo[FBSDKErrorLocalizedTitleKey] ?: #"Oops!";
[[[UIAlertView alloc] initWithTitle:title message:message delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil] show];
}
- (void)sharerDidCancel:(id<FBSDKSharing>)sharer
{
NSLog(#"share cancelled");
}
Console output:
The only message I get back after posting is after a few seconds this message appears:
plugin com.apple.share.Facebook.post invalidated
Please help!
Footnote: appDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Initialize Parse.
[Parse enableLocalDatastore];
[Parse setApplicationId:#"XXXX"
clientKey:#"XXX"];
[PFFacebookUtils initializeFacebookWithApplicationLaunchOptions:launchOptions];
//Initialize Facebook
[FBSDKAppEvents activateApp];
return [[FBSDKApplicationDelegate sharedInstance] application:application didFinishLaunchingWithOptions:launchOptions];
}
//Method added for facebook integration
- (BOOL)application:(UIApplication *)application
openURL:(NSURL *)url
sourceApplication:(NSString *)sourceApplication
annotation:(id)annotation {
return [[FBSDKApplicationDelegate sharedInstance] application:application
openURL:url
sourceApplication:sourceApplication
annotation:annotation];
}
- (void)applicationDidBecomeActive:(UIApplication *)application {
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
[FBSDKAppEvents activateApp];
}
I think I am too late answering this question but someone else could trap into this as well, that's why sharing my knowledge.
As per Facebook API documentation
sharer:didCompleteWithResults: Sent to the delegate when the share completes without error or cancellation.
The results from the sharer. This may be nil or empty.
its probably because this delegate method is only get called when the post is successfully shared. In case of failure the other delegate method sharer:didFailWithError:get called. I think Facebook API should not need to add the result parameter is that case.
So in my experience if sharer:didCompleteWithResults whenever this is called that would mean success.

Trouble verifying Facebook publish_actions permission

I have successfully logged into Facebook and asked for publish_actions permission. The permission is granted, as I can readily verify on https://www.facebook.com/settings?tab=applications and also by calling
-(void) whatPermissionsDoIHave
{
[FBRequestConnection startWithGraphPath:#"/me/permissions"
completionHandler:^(
FBRequestConnection *connection,
NSDictionary* result,
NSError *error
) {
//do checks in here
}
}];
}
So I know for certain that I have permission. However, whenever it's time to share, my permission check returns false. I will now show my permission check
+ (BOOL)hasWritePermissions
{
if (!FBSession.activeSession.isOpen) return NO;
NSLog(#"So the session is at least open");
return [FBSession.activeSession.permissions indexOfObject:#[#"publish_actions"]] != NSNotFound;
}
So am I missing something else? Perhaps something I should have done in AppDelegate or such? Thanks for any insight.
Right now the only call I am making in AppDelegate is
- (BOOL)application:(UIApplication *)application
openURL:(NSURL *)url
sourceApplication:(NSString *)sourceApplication
annotation:(id)annotation {
// attempt to extract a token from the url
return [FBAppCall handleOpenURL:url
sourceApplication:sourceApplication
fallbackHandler:^(FBAppCall *call) {
NSLog(#"In fallback handler");
}];
}
The permissions property is an array of strings, but you're calling indexOfObject on another array, which is probably why it can't find it. Try this instead:
return [FBSession.activeSession.permissions indexOfObject:#"publish_actions"] != NSNotFound;

Scringo and Parse: how to perform simultaneous Facebook login?

My app allows typical registration (e-mail,username,password, etc), but also enables Facebook/Twitter signup/singin via Parse. If at all possible, I'd like to avoid forcing a user to sign in twice if they sign in with Facebook or Twitter. Currently, I've added "Scringo's" programmatic signup capability to sign users into "scringo" with the same credentials they use for my app; however. What if they sign up with Facebook? It seems very confusing for a user to sign in with say, Facebook, and then slide a bar over, and to access the messaging component to be forced to login with Facebook again.
Here's what I have currently, is there anyway to link the Facebook/Twitter signin process so that both Parse gets the information along with Scringo in the press of one button?
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[Parse setApplicationId:#"...gzPI7Omoxc..."
clientKey:#"...s22ZqIbM7n..."];
[PFTwitterUtils
initializeWithConsumerKey:#"...W4XjjpHz..."
consumerSecret:#"...Czjtht7v5..."];
[PFAnalytics trackAppOpenedWithLaunchOptions:launchOptions];
[PFFacebookUtils initializeFacebook];
[self configureNavigationBar];
[Scringo initWithAppId:#"...N2cCMhW1Bv..." completion:^{
[Scringo addSidebar:self.window toLeft:YES];
PFUser *currentUser = [PFUser currentUser];
if (! [ScringoUser currentUser].isAuthenticated) {
[ScringoUser signUpWithEmail:currentUser.email userName:currentUser.username password:#"notforyou" completion:^(ScringoUser *aUser, BOOL isSuccess) {
if (isSuccess) {
[currentUser setObject:aUser.userId forKey:#"ScringoUserId"];
[currentUser saveInBackground];
}
}];
}
}];
return YES;
}
Turns out there is a method in the SDK (I simply overlooked it)
[Scringo connectFacebook]
If the app has successfully logged into facebook already, calling this authenticates Scringo as well.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[Parse setApplicationId:#"gzPI7OmoxcSYqQGWASjzVd8lP5q8FVJqh2ZLsXnp"
clientKey:#"s22ZqIbM7n8SN1End57vmTmmeL55YnIen7dmMhIR"];
[PFTwitterUtils
initializeWithConsumerKey:#"W4XjjpHzG03bWK8R0iPvs27Yy"
consumerSecret:#"Czjtht7v58yfZ00QV5Gkumfdy8RauCyPllYh5SPm00I3M6fkPP"];
[PFAnalytics trackAppOpenedWithLaunchOptions:launchOptions];
[PFFacebookUtils initializeFacebook];
[self configureNavigationBar];
[Scringo initWithAppId:#"cvOV3lLwQN2cCMhW1BvnW2eWnyoiLNgA" completion:^{
[Scringo addSidebar:self.window toLeft:YES];
PFUser *currentUser = [PFUser currentUser];
if (! [ScringoUser currentUser].isAuthenticated) {
if (currentUser) {
if (currentUser.email) [Scringo connectFacebook];
else
[ScringoUser signUpWithEmail:currentUser.email userName:currentUser.username password:#"notforyou" completion:^(ScringoUser *aUser, BOOL isSuccess) {
if (isSuccess) {
[currentUser setObject:aUser.userId forKey:#"ScringoUserId"];
[currentUser saveInBackground];
}
}];
}
}
}];
return YES;
}
That's assuming a PFUser already exists, so I also added the same call to the login page for users logging in with facebook for the first time
- (void)logInViewController:(PFLogInViewController *)logInController didLogInUser:(PFUser *)user {
[self dismissViewControllerAnimated:YES completion:^{
if (!self.currentUser.email) [Scringo connectFacebook];
}];
}
Tested and working.

Continue log in when internet connection reestablished. Facebook SDK IOS

I got an app that is connected to Facebook.
Upon start up of the app, if not logged out, the user will proceed to the home controller
, else he will stay in the login controller and asked to login.
The scenario:
User open the app with no internet connection.
The app will show an alertview saying that there is no connection.
The connection is established while the app is running.
The problem:
The problem is for user who have logged in and have an active FB session. The user is left hanging in the log in page even if the connection is established. He can only enter the home controller by logging out and logging in again or terminating the app.
What I want to accomplish:
I want that when the connection is established, the login will continue. When the FBToken is collected, my codes will automatically redirect the user to the home controller. How can I do that?
I want a simple code that will continue the interrupted login due to no internet connection.
At app delegate (these codes are responsible for checking if there is an active fb session):
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Whenever a person opens the app, check for a cached session
if (FBSession.activeSession.state == FBSessionStateCreatedTokenLoaded) {
// If there's one, just open the session silently, without showing the user the login UI
[FBSession openActiveSessionWithReadPermissions:#[#"basic_info"]
allowLoginUI:YES
completionHandler:^(FBSession *session, FBSessionState state, NSError *error) {
// Handler for session state changes
// This method will be called EACH time the session state changes,
// also for intermediate states and NOT just when the session open
[self sessionStateChanged:session state:state error:error];
}];
}
return YES;
}
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation
{
// Note this handler block should be the exact same as the handler passed to any open calls.
[FBSession.activeSession setStateChangeHandler:
^(FBSession *session, FBSessionState state, NSError *error) {
// Retrieve the app delegate
AppDelegate* appDelegate = [UIApplication sharedApplication].delegate;
// Call the app delegate's sessionStateChanged:state:error method to handle session state changes
[appDelegate sessionStateChanged:session state:state error:error];
}];
return [FBAppCall handleOpenURL:url sourceApplication:sourceApplication];
}
- (void)sessionStateChanged:(FBSession *)session state:(FBSessionState) state error:(NSError *)error
{
// If the session was opened successfully
if (!error && state == FBSessionStateOpen){
// Show the user the logged-in UI
[self userLoggedIn];
return;
}
if (state == FBSessionStateClosed || state == FBSessionStateClosedLoginFailed){
// If the session is closed
// Show the user the logged-out UI
[self userLoggedOut];
}
// Handle errors
if (error){
NSLog(#"Error");
NSString *alertText;
NSString *alertTitle;
// If the error requires people using an app to make an action outside of the app in order to recover
if ([FBErrorUtility shouldNotifyUserForError:error] == YES){
alertTitle = #"Something went wrong";
alertText = [FBErrorUtility userMessageForError:error];
[self showMessage:alertText withTitle:alertTitle];
} else {
// If the user cancelled login, do nothing
if ([FBErrorUtility errorCategoryForError:error] == FBErrorCategoryUserCancelled) {
NSLog(#"User cancelled login");
// Handle session closures that happen outside of the app
} else if ([FBErrorUtility errorCategoryForError:error] == FBErrorCategoryAuthenticationReopenSession){
alertTitle = #"Session Error";
alertText = #"Your current session is no longer valid. Please log in again.";
[self showMessage:alertText withTitle:alertTitle];
// Here we will handle all other errors with a generic error message.
// We recommend you check our Handling Errors guide for more information
// https://developers.facebook.com/docs/ios/errors/
} else {
//Get more error information from the error
NSDictionary *errorInformation = [[[error.userInfo objectForKey:#"com.facebook.sdk:ParsedJSONResponseKey"] objectForKey:#"body"] objectForKey:#"error"];
// Show the user an error message
alertTitle = #"Something went wrong";
alertText = [NSString stringWithFormat:#"Please retry. \n\n If the problem persists contact us and mention this error code: %#", [errorInformation objectForKey:#"message"]];
[self showMessage:alertText withTitle:alertTitle];
}
}
// Clear this token
[FBSession.activeSession closeAndClearTokenInformation];
// Show the user the logged-out UI
[self userLoggedOut];
}
}
Now, in the in the log in page:
- (void)loginViewShowingLoggedInUser:(FBLoginView *)loginView {
//here is where is display modal the home view controller when the session token is successfully loaded
}
When there is no connection and the app failed to load the logged in user, the app is stuck in the log in viewcontroller. What code can I implement that when the connection goes back, the app will continue to log in (if a session is active).
Like:
if(connectionIsBack == Yes && session is active) {
//relogin;
}
I already have the code to handle connection status changes (Reachability). Just need the code to relogin. I tried calling the codes from the app delegate but nothing happens.
Thanks!

Call open Url in appDelegate

I have this code in my app delegate:
- (BOOL)application:(UIApplication *)application
openURL:(NSURL *)url
sourceApplication:(NSString *)sourceApplication
annotation:(id)annotation {
// attempt to extract a token from the url
self.openedURL = url;
// attempt to extract a token from the url
return [FBSession.activeSession handleOpenURL:url];
}
how can I call it from a UIViewController??
EDIT:
- (IBAction)authButtonAction:(id)sender {
/*
AppDelegate *appDelegate =
[[UIApplication sharedApplication] delegate];*/
// The user has initiated a login, so call the openSession method
// and show the login UX if necessary.
//[appDelegate openSessionWithAllowLoginUI:YES];
// If the user is authenticated, log out when the button is clicked.
// If the user is not authenticated, log in when the button is clicked.
if (FBSession.activeSession.isOpen) {
[FacebookDialogueViewControllerDelegate closeSession];
} else {
// The user has initiated a login, so call the openSession method
// and show the login UX if necessary.
[FacebookDialogueViewControllerDelegate openSessionWithAllowLoginUI:YES];
}
}
...the view controller then has this:
- (BOOL)openSessionWithAllowLoginUI:(BOOL)allowLoginUI {
return [FBSession openActiveSessionWithReadPermissions:nil
allowLoginUI:allowLoginUI
completionHandler:^(FBSession *session,
FBSessionState state,
NSError *error) {
[self sessionStateChanged:session
state:state
error:error];
}];
}
You have two options:
Pass the NSURL into the UIViewController on instantiation or otherwise.
From your UIViewController, get a reference to the app delegate and the use the openedURL property.
Code for Option 2
MyViewController *controller = [[UIApplication sharedApplication] delegate];
controller.openedURL;
You don't. This gets called by the OS when you register the URL syntax in the plist of your app.
You can then use this method to deal with the request.
i.e. if you have a wordsearch app then you may have it so that a user can open a wordsearch from a link in an email like...
wordsearch://thisisthecontetnofthewordsearchhfsadkljfhasdkfjahsdkfjhaskljasdhflkjahsgdflkjadhslfkj
Then you would use this function to parse the string and turn it into an actual wordsearch puzzle (or something like this).
What is it you are actually wanting to do from your UIViewController?

Resources