Facebook's FBConnect SDK issues on iOS - iphone

I'm using FBConnect sdk in order to publish posts to a user's profile via my application. I'm having a number of problems with this:
When the relevant code runs for the first time on the device, the user is re-directed, as wanted, to the facebook app/website, which asks him to authorize it. if the user authorizes it, it returns back to the application, which pops a "Connect to facebook" view controller which asks the user to log in. this is weird, as the user is already logged in, otherwise how could he authorize the app? but I guess this may be ok, as he hadn't logged in through the app yet. after he logs in, it does nothing. only the second time the code gets run, after he authorized the app, the user gets the posting dialog.
If the user hadn't authorized the app, when it comes back to my app after the authorization dialog, it asks the user to login ( just as if he authorized ), and does nothing after he had logged in. only the second time the code gets ran, the authorization dialog opens, with the optinos "Authorize" & "Leave App", instead of "Authorize" & "Don't authorize" / "Allow" & "Don't Allow".
In addition, if the user has deleted his authorization via his account's settings on facebook, instead of just asking him to re-authorize it, a facebook dialog pops ( instead of the post/authorization dialog ), saying: "An error occurred. Please try again later." Trying later doesn't help. it will pop always, even if u restart the app. the only way to make it go away is to re-install the app, which will cause it to re-pop the authoriziation dialog.
So here's what I want to achieve:
After the user authorizes the app, he wouldn't have to log in again.
After the user authorizes the app, the posting dialog will pop immedietly, without him having to re-run the code ( which is triggered, btw, with a button ).
If the user un-authorizes the app, he will be prompted again with the authorization dialog, instead of the error dialog
If he refused the authorization, I will call a function that displays an error/etc.
Here's the relevant code:
MyViewController.m
- (void)shareOnFacebook
{
Facebook *facebook = [[Facebook alloc] initWithAppId:myAppID];
[(MyAppDelegate *)[[UIApplication sharedApplication] delegate] setFacebook:facebook];
[facebook release];
facebook = [(MyAppDelegate *)[[UIApplication sharedApplication] delegate] facebook];
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
if ([defaults objectForKey:#"FBAccessTokenKey"] && [defaults objectForKey:#"FBExpirationDateKey"]) {
facebook.accessToken = [defaults objectForKey:#"FBAccessTokenKey"];
facebook.expirationDate = [defaults objectForKey:#"FBExpirationDateKey"];
}
if (![facebook isSessionValid]) {
[facebook authorize:[NSArray arrayWithObjects:#"publish_stream", nil] delegate:(MyAppDelegate *)[[UIApplication sharedApplication] delegate]];
}
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
//Giving the dictionary some parameters for posting defaults
[facebook dialog:#"feed" andParams:dictionary andDelegate:self]; //Note: we have 2 different delegates! appDelegate for connections & url switching, and self for dialogs
}
MyAppDelegate.h
#interface MyAppDelegate : NSObject <UIApplicationDelegate, FBSessionDelegate, FBDialogDelegate>
{
Facebook *facebook; // kept for facebook sharing, accessed only from MyViewController although delegate methods are handeled in here
}
#property (nonatomic, retain) Facebook *facebook;
#end
MyAppDelegate.m
- (void)fbDidLogin
{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:[facebook accessToken] forKey:#"FBAccessTokenKey"];
[defaults setObject:[facebook expirationDate] forKey:#"FBExpirationDateKey"];
[defaults synchronize];
}
- (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url
{
return [self.facebook handleOpenURL:url];
}
Is it possible that I'm missing a couple of delegate functions on MyViewController? Because I see for some reason I've marked it as implementing the FBDialogDelegate protocol, although he doesn't implement any function from there.
I'd be really glad if you guys would help me, as this is extremely frustrating for me. I couldn't find nothing about this on the internet, and I feel like im drowning in here.Tnx in advance!

First:
[facebook authorize:[NSArray arrayWithObjects:#"publish_stream",#"offline_access",nil] delegate:self];
The offline_access key here will keep your auth token alive forever (or, more specifically, until the user manually de-authorizes your application in their application settings in their Facebook account settings). Also, set your active VC as the delegate (more on that later).
Secondly:
-(void)popUserShareFeed {
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
//Giving the dictionary some parameters for posting defaults
[facebook dialog:#"feed" andParams:dictionary andDelegate:self];
}
Call this method (or one like it) in your -fbDidLogin delegate method. Also call it in your original method if the session was still valid, i.e.:
if (![facebook isSessionValid]) {
[facebook authorize:[NSArray arrayWithObjects:#"publish_stream", nil] delegate:(MyAppDelegate *)[[UIApplication sharedApplication] delegate]];
} else {
[self popUserShareFeed];
}
...and your new fbDidLogin:
- (void)fbDidLogin
{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:[facebook accessToken] forKey:#"FBAccessTokenKey"];
[defaults setObject:[facebook expirationDate] forKey:#"FBExpirationDateKey"];
[defaults synchronize];
[self popUserShareFeed];
}
(Note: you'll have to define this in MyViewController.m and use it as your FBSessionDelegate. It will be functionally equivalent. Your AppDelegate does not need to also be your FBSessionDelegate.
Third:
Implement the -fbDidNotLogin:(BOOL)cancelled FBSessionDelegate method, like so:
-(void)fbDidNotLogin:(BOOL)cancelled {
if (cancelled) {
... some alert about the user cancelling...
} else {
... some alert about how it failed for some reason...
}
}
Fourth, as far as your bizarro errors go: A) the Facebook SDKs in general are not great, and B) I'd only set the auth token and the expiration date on your facebook object if
A) you don't already have one instantiated (i.e., it's nil)
and
B) the expirationDate you're setting is in the future (i.e. timeIntervalSinceNow [the NSDate instance method] called on it returns > 0).

Sounds like you're experiencing the same issue described in this Facebook Platform Developer forum post. I'm encountering the same problem, but on the web. Only one response was given from Facebook in that thread, and it's wrong information.
Facebook has the worst developer docs and developer support ever, I wouldn't hold my breath waiting on a solution.

I had similar but not the same problem: message "An error occurred. Please try again later." was shown always.
The problem was with not properly configured App ID - it was provided by customer (so I'm not sure what exactly was wrong); everything works properly since I replaced it with my own test App ID (from previous app).

I have integrated FBConnect in so many applications , but never face such kind of critical issue. So, there would be something missing in your code:
Insted of checking FBAccessTokenKey & FBExpirationDateKey, simply try with session object of FBSession class of FBConnect.
Just try using mine code with few conidtions:
session = [[FBSession sessionForApplication:#"key" secret:#"AppSecretKey" delegate:self] retain];
[session resume];
_posting = YES;
// If we're not logged in, log in first...
if (![session isConnected]) {
loginDialog = nil;
loginDialog = [[FBLoginDialog alloc] init];
[loginDialog show];
}
// If we have a session and a name, post to the wall!
else if (_facebookName != nil) {
[self postToWall]; // Over here posting dialog will appear , if user has already logged in or session is running.
}
}
else {
[FBBtn setTitle:#"Login" forState:UIControlStateNormal]; // if you want to change the title of button
[session logout];
}
Additionally take care of releasing the session object in dealloc method only , rather than releasing it in any other method.
- (void)dealloc {
[super dealloc];
[session release];
session = nil;
}
Hope that would solve your problem.

Not a direct solution for your code problems, but...
Have you consider using an open-source library for this? ShareKit is a great example: http://getsharekit.com . The integration is dead simple and it does everything you need.

I was having the exact same issue, and used Ben Mosher's response above, but I also decided to create a simple singleton class for the Facebook object, so that in my AppDelegate I can handle the following function properly:
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url
sourceApplication:(NSString *)sourceApplication annotation:(id)annotation {
return [[[FBManager defaultManager] facebook] handleOpenURL:url];
//return [facebook handleOpenURL:url];
}
My singleton class is both FBSessionDelegate and FBDialogDelegate, so it handles the corresponding methods.
This allows me to only prompt the user to login when they are actually trying to post something, instead of when the app launches.

Related

Facebook always reauthorizing on each app launch?

I have recently added Facebook integration to my app, and everything works fine except when the user relaunches the app. Each time the app is restarted, Facebook has to go back through its authorization process. This involves switching out of the app to Safari/Facebook, then back to my app. How can I make Facebook save the info, or be able to get blanket permissions for my app so that I doesn't constantly reauthorize?
Here is my code from the applicationDidFinishLaunchingWithOptions: method:
facebook = [[Facebook alloc] initWithAppId:#"203604286395694" andDelegate:self];
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
if ([defaults objectForKey:#"FBAccessTokenKey"]
&& [defaults objectForKey:#"FBExpirationDateKey"]) {
facebook.accessToken = [defaults objectForKey:#"FBAccessTokenKey"];
facebook.expirationDate = [defaults objectForKey:#"FBExpirationDateKey"];
}
if (![facebook isSessionValid]) {
[facebook authorize:nil];
}
Relevant Facebook delegate methods:
- (void)fbDidLogin {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:[facebook accessToken] forKey:#"FBAccessTokenKey"];
[defaults setObject:[facebook expirationDate] forKey:#"FBExpirationDateKey"];
[defaults synchronize];
}
- (void) requestDialogButtonClicked {
NSMutableDictionary* params =
[NSMutableDictionary dictionaryWithObjectsAndKeys:
#"invites you to check out cool stuff", #"message",
#"Check this out", #"notification_text",
nil];
[facebook dialog:#"apprequests"
andParams:params
andDelegate:self];
}
- (void)dialogDidComplete:(FBDialog *)dialog {
NSLog(#"dialog completed successfully");
}
Are you calling Facebook authorize: somewhere? That's how the fbDidLogin will be triggered. Assuming you are, the behavior indicates that either the token or the date (or both) is nil, or that the date is past expiration (unlikely).
Log the values upon writing and reading the user defaults. My guess is that the authorize either wasn't called, or failed (due to app key or some other problem not shown in the code), leaving the fbDidLogin method uncalled or saving nil values.
That's because you are not asking for permissions when you authorize your app .. "offline_access" provide a long term access token so you will not need to get a new access token every time the user wants to use the Facebook .. so what you need to authorize your app like this
if (![facebook isSessionValid]) {
NSArray *permissions = [[NSArray alloc] initWithObjects:
#"user_likes",
#"read_stream",
#"offline_access"
nil];
[facebook authorize:permissions];
[permissions release];
}
Edit :
Please check the following in your implementation :
1.you need to add the following functions in your application delegate(the Facebook object here is your instance value of FaceBook Class).
// Pre 4.2 support
- (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url
{
return [facebook handleOpenURL:url];
}
// For 4.2+ support
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url
sourceApplication:(NSString *)sourceApplication annotation:(id)annotation
{
return [facebook handleOpenURL:url];
}
2.In your info.plist add to URL types > URL Schemes > your Facebook app ID with fb prefix (finally your value will be like this for ex. fb313714***).

iPhone Facebook app: Where does the "permissions array" code go?

I just went through the "Getting Started > Mobile Apps" documentation on Facebook developers. They provided the code to ask permission for accessing information but they don't specify where that code is suppose to go.
Could you tell me where to put the code? Because I don't want to put it in the wrong space.
The code I want to add:
NSArray* permissions = [[NSArray arrayWithObjects:
#"publish_stream", #"offline_access", nil] retain];
[facebook authorize:permissions delegate:self];
My appDelegate code:
#import "iOSTestAppDelegate.h"
#implementation iOSTestAppDelegate
#synthesize facebook;
#synthesize viewController=_ViewController;
#synthesize window=_window;
#synthesize managedObjectContext=__managedObjectContext;
#synthesize managedObjectModel=__managedObjectModel;
#synthesize persistentStoreCoordinator=__persistentStoreCoordinator;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch.
[self.window makeKeyAndVisible];
/* Step 2. Within the body of the application:didFinishLaunchingWithOptions: method create instance of the Facebook class using your app id */
facebook = [[Facebook alloc] initWithAppId:#"********"];
/* Step 3. Once the instance is created, check for previously saved access token information. */
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
if ([defaults objectForKey:#"FBAccessTokenKey"]
&& [defaults objectForKey:#"FBExpirationDateKey"]) {
facebook.accessToken = [defaults objectForKey:#"FBAccessTokenKey"];
facebook.expirationDate = [defaults objectForKey:#"FBExpirationDateKey"];
}
/* Step 4. Check for a valid session and if it is not valid call the authorize method which will both signin the user and prompt the user to authorize the app: */
if (![facebook isSessionValid]) {
[facebook authorize:nil delegate:self];
}
return YES;
}
/* Step 5. Add the application:handleOpenURL: method to the AppDelegate with a call to the facebook instance: */
- (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url {
return [facebook handleOpenURL:url];
}
/* Step 6. Implement the fbDidLogin method from the FBSessionDelegate implementation. In this method you will save the user's credentials specifically the access token and corresponding expiration date. */
- (void)fbDidLogin {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:[facebook accessToken] forKey:#"FBAccessTokenKey"];
[defaults setObject:[facebook expirationDate] forKey:#"FBExpirationDateKey"];
[defaults synchronize];
}
...
#end
After allocate your facebook instance, you have to provide these permission for authorizing the facebook from the user_credentials. it means were getting these permission from the user who are login.
facebook=[[Facebook alloc]initWithAppId:kAppId];
_permissions = [[NSArray arrayWithObjects:#"publish_stream",#"offline_access",nil]retain];
[facebook authorize:_permissions delegate:self];
Publish stream provides : Enables your app to post content, comments, and likes to a user's stream and to the streams of the user's friends. With this permission, you can publish content to a user's feed at any time, without requiring offline_access. However, please note that Facebook recommends a user-initiated sharing model.
Offline Access:
Enables your app to perform authorized requests on behalf of the user at any time. By default, most access tokens expire after a short time period to ensure applications only make requests on behalf of the user when the are actively using the application. This permission makes the access token returned by our OAuth endpoint long-lived.
Refer more details about permission here

facebook isSessionValid is always NO

hi i use the newest facebook api with SSO and do implement like following in my viewController (not in the appDelegate like in the example if that makes any difference):
in my .h file
Facebook *facebook;
#property (nonatomic,retain) Facebook *facebook;
in my .m file i do the following in my viewDidLoad
self.facebook = [[Facebook alloc] initWithAppId:#"myappID"];
in another method I do
if (![facebook isSessionValid]) {
NSArray *permissions = [[NSArray arrayWithObjects:#"publish_stream",nil] retain];
[facebook authorize:permissions delegate:self];
}
but it seems that isSessionValid is alway NO
and I don't know why!
does anybody have any idea why ?
oh and also expirationDate and accessTokken are nil
I had this exact same problem yesterday, it seems you must persist and restore the access token/expiration date yourself.
In your viewDidLoad:
// Restore previously saved Facebook credentials (If any)
facebook.accessToken = [[NSUserDefaults standardUserDefaults] stringForKey:#"FBAccessToken"];
facebook.expirationDate = (NSDate *) [[NSUserDefaults standardUserDefaults] objectForKey:#"FBExpirationDate"];
// Trigger SSO Facebook authentication if required
if ([facebook isSessionValid] == NO) {
[facebook authorize:nil delegate:self];
} else {
[self fbDidLogin];
}
Then in the fbDidLogin delegate:
- (void)fbDidLogin
{
// Save the users access token and expiration date so we can restore it later
// This way we can avoid needlessly authenticating to Facebook.
[[NSUserDefaults standardUserDefaults] setObject:self.facebook.accessToken forKey:#"FBAccessToken"];
[[NSUserDefaults standardUserDefaults] setObject:self.facebook.expirationDate forKey:#"FBExpirationDate"];
// User has logged in to Facebook, now get their userId from Facebook
[facebook requestWithGraphPath:#"me" andDelegate:self];
}
So now users will have to login/authorize your application once and from then on their session will remain valid until they logout of Facebook.
alright here is the answer to my problem.
as I said in the comment also the application:handleOpenURL: never got called!
cause I had this method in my ViewController and apparently it has to be in the appDelegate!
so in my Navigationbar based application I did the following in my appDelegate:
in the .h file:
ViewController *viewController;
#property (nonatomic, retain) IBOutlet ViewController *viewController;
in the .m file:
#synthesize viewController;
viewController = [[ViewController alloc]init];
[self.navigationController pushViewController:viewController animated:NO];
and then implemented the method like that:
- (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url {
return [viewController.facebook handleOpenURL:url];
}
and of course in my ViewController I did everything #Tyler mentioned (thank you for that)
and everything works like a charm.
hope this helps everyone with the same problem.

Saving Facebook access_token in NSUserDefaults on iOS

iOS beginner here. I'm using the following code to save my facebook accessToken and expirationDate in NSUserDefaults:
facebook = [[Facebook alloc] initWithAppId:#"225083222506272"];
[facebook authorize:nil delegate:self];
NSString *access=[facebook accessToken];
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSDictionary *appDefaults = [NSDictionary dictionaryWithObjectsAndKeys:access, #"accessToken",[facebook expirationDate], #"expirationDate",nil];
[defaults registerDefaults:appDefaults];
And I'm trying to retrieve accessToken and expirationDate in a later call with:
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSString *access=[defaults valueForKey:#"accessToken"];
NSDate *date=[defaults objectForKey:#"expirationDate"];
[facebook fbDialogLogin:access expirationDate:date];
but access and date are null. What am I doing wrong?
The code here is not synchronous. It means it does not block after the call to [facebook authorize:nil delegate:self];. You should instead implement the fbDidLogin delegate method to be notified of when the user has actually logged in successfully. At that point, retrieve the access tokens and save them to user defaults.
Here's a partial sample:
- (void)userClickedFacebookLogin {
[facebook authorize:nil delegate:self]; // delegate is self
}
// Delegate method that you should implement to get notified
// when user actualy logs in.
- (void)fbDidLogin {
// now get the access token and save to user defaults
NSString *access = [facebook accessToken];
// ..
}
Also make sure that the class which has the above code implements the FBSessionDelegate protocol at minimum.
#interface MyClass <FBSessionDelegate> {
}
#end
Look at the DemoApp sample and specifically the DemoAppViewController class from Facebook to get a better idea.

facebook iOS sdk custom delegate and SSO authorization

I've been toying with the new facebook iOS sdk. I have gotten my project to the point where someone can login successfully. However I have 2 questions:
1) to hit the graph api you issue the following call: [facebookInstance requestWIthGraphPath:#"me" andDelegate:self]. Is it possible to specificy a delegate other than self? Currently all responses go to the (void)request: (FBRequest *) request didLOad:(id) result. But since my app may issue requests to the facebook api at different times and need different things to happen for each respective request issued, how can I specifiy which callback function the response should hit in my app? Is this possible?
2) Once the user has logged in, how can you check their authorization/login status so that I can disable the login button if they are already logged in? Consider the example of a user turning on the app for the 1st time and logging in. Then closing the app, and opening a few minutes later. I rather not show the user the login button the 2nd time and instead start pulling information to display such as their name.
1) There shouldn't be anything wrong with specifying a delegate other than self, as long as the object you provide as the delegate conforms to the FBRequestDelegate protocol. Alternately you can interrogate the FBRequest object you get in the delegate method to determine which request it was that just loaded and what the appropriate response is.
2) To make it so the user stays logged in, you need to save the accessToken (an NSString) and the expirationDate (an NSDate) properties of the Facebook object when the user logs in. Then, when you would log your user in, attempt to restore these values.
Some code snippets that may help:
- (void)fbDidLogin
{
NSString *tokenString = [[self facebook] accessToken];
NSDate * expirationDate = [[self facebook] expirationDate];
[[NSUserDefaults standardUserDefaults] setObject: tokenString forKey:#"FacebookToken"];
[[NSUserDefaults standardUserDefaults] setObject: expirationDate forKey:#"FacebookExpirationDate"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
And, when you need to log the user in:
NSString *tokenString = [[NSUserDefaults standardUserDefaults] objectForKey:#"FacebookToken"];
NSDate *expDate = [[NSUserDefaults standardUserDefaults] objectForKey:#"FacebookExpirationDate"];
if (tokenString != nil && expDate != nil)
{
[facebook setAccessToken:tokenString];
[facebook setExpirationDate:expDate];
}
if ([facebook isSessionValid])
{
//Your session is valid, do whatever you want.
}
else
{
//Your session is invalid
//(either the session is past the expiration date or there is no saved data for the login)
//you need to ask the user to log in
}