I am trying to integrate the new Facebook SDK for iOS and had a problem understanding some concepts.
I authenticate using [FBSession sessionOpenWithPermissions:...]
auth dialog comes up and I return the app. Auth successful.
Then shut down the app, relaunch again. [[FBSession activeSession] accessToken]
successfully returns previously saved token.
However, at the same time, [[FBSession activeSession] isOpen]
returns NO
. (This means session is not ready for use.)
Also, [[FBSession activeSession] state]
is FBSessionStateCreatedTokenLoaded
at this time. Tutorial here uses isOpen
call to verify active session is loaded and opened with token.
So what do we call to open the token-loaded session without redirecting user to auth dialog?
Hints:
In FBSessionState
enum, for FBSessionStateOpen
it says:
Open session state indicating user has logged in or a cached token is available.
However FBSessionStateCreatedTokenLoaded
is described as:
One of two initial session states indicating that a cached token was loaded; when a session is in this state, a call to open* will result in an open session, without UX or app-switching
Could you please help me figure out understanding these session transitions?
I'm including a Facebook utility class I wrote that helps with understanding login state, because I expose a "logged in" / "not logged in" message to the user in my own Settings UI, in addition to using the actual 'FBLoginView' component when it comes time to allow the user to toggle authorization state.
The code below is also available via this gist.
I may not have interpreted all the FBSessionState
types correctly in my switch statement, but thus far, it's served me well in the cases I've tested (I've just put this together).
The key thing to note, that others have alluded to, is that sometimes you have a cached authorization token that you can't use immediately, but if you make a Facebook open
call on it, you can get it to be reusable (refreshed). This open call works behind the scenes, without triggering any UI / jarring OAuth window/app switching (if you have a cached token).
See my comments in the method isLoggedInAfterOpenAttempt
. Note how I check for the state to be FBSessionStateCreatedTokenLoaded
and only then, make the call to
-openWithCompletionHandler:^(FBSession *session, FBSessionState status, NSError *error)
.
Other tidbits about this class:
FBGraphUser
. It's not used in any of the login methods demonstrated here, though.#import "UWFacebookService.h" @implementation UWFacebookService // Static static const int ddLogLevel = LOG_LEVEL_DEBUG; // Strong @synthesize facebookGraphUser = _facebookGraphUser; #pragma mark - Inquiries - (BOOL)isSessionStateEffectivelyLoggedIn:(FBSessionState)state { BOOL effectivelyLoggedIn; switch (state) { case FBSessionStateOpen: log4Info(@"Facebook session state: FBSessionStateOpen"); effectivelyLoggedIn = YES; break; case FBSessionStateCreatedTokenLoaded: log4Info(@"Facebook session state: FBSessionStateCreatedTokenLoaded"); effectivelyLoggedIn = YES; break; case FBSessionStateOpenTokenExtended: log4Info(@"Facebook session state: FBSessionStateOpenTokenExtended"); effectivelyLoggedIn = YES; break; default: log4Info(@"Facebook session state: not of one of the open or openable types."); effectivelyLoggedIn = NO; break; } return effectivelyLoggedIn; } /** * Determines if the Facebook session has an authorized state. It might still need to be opened if it is a cached * token, but the purpose of this call is to determine if the user is authorized at least that they will not be * explicitly asked anything. */ - (BOOL)isLoggedIn { FBSession *activeSession = [FBSession activeSession]; FBSessionState state = activeSession.state; BOOL isLoggedIn = activeSession && [self isSessionStateEffectivelyLoggedIn:state]; log4Info(@"Facebook active session state: %d; logged in conclusion: %@", state, (isLoggedIn ? @"YES" : @"NO")); return isLoggedIn; } /** * Attempts to silently open the Facebook session if we have a valid token loaded (that perhaps needs a behind the scenes refresh). * After that attempt, we defer to the basic concept of the session being in one of the valid authorized states. */ - (BOOL)isLoggedInAfterOpenAttempt { log4Debug(@"FBSession.activeSession: %@", FBSession.activeSession); // If we don't have a cached token, a call to open here would cause UX for login to // occur; we don't want that to happen unless the user clicks the login button over in Settings, and so // we check here to make sure we have a token before calling open if (FBSession.activeSession.state == FBSessionStateCreatedTokenLoaded) { log4Info(@"We have a cached token, so we're going to re-establish the login for the user."); // Even though we had a cached token, we need to login to make the session usable: [FBSession.activeSession openWithCompletionHandler:^(FBSession *session, FBSessionState status, NSError *error) { log4Info(@"Finished opening login session, with state: %d", status); }]; } else { log4Info(@"Active session wasn't in state 'FBSessionStateCreatedTokenLoaded'. It has state: %d", FBSession.activeSession.state); } return [self isLoggedIn]; } @end
This means that when you have a token saved (cached) or otherwise already available, the Facebook iOS library still requires you to use the open method to reinitialise a session.
Doing so will if it can reuse an existing token (and this is your case), and in this case the UX (user experience - ie, app switching or Facebook login popup) will not be triggered.
The user impression is that he never logged out, but in the app what is happening is you are contacting Facebook to reopen a session.
The reason this is designed as such is because in the case the token is available but expired, the Facebook library will tell you - "hey token expired, consider yourself logged out unless you get a new token now."
Hope that helps.
Try following code sample:
/////////////////////////////////
-(void)CallBackOpenURLFromDelegate:(NSURL *)url
{
[FBSession.activeSession handleOpenURL:url];
}
-(id)init
{
self = [super init];
FBSessionTokenCachingStrategy* pToken = [[[FBSessionTokenCachingStrategy alloc]initWithUserDefaultTokenInformationKeyName:@"STokenInfoX"]autorelease];
FBSession.activeSession = [[FBSession alloc] initWithAppID:FACEBOOK_AppId
permissions:[NSArray arrayWithObject:@"status_update"]
urlSchemeSuffix:@""
tokenCacheStrategy: pToken];
return self;
}
-(void)dealloc
{
[FBSession.activeSession close];
[super dealloc];
}
-(void) UploadImpl:(NSString*)strImagePath
{
FBRequest *photoUploadRequest = [FBRequest requestForUploadPhoto: [UIImage imageWithContentsOfFile:strImagePath ]];
[photoUploadRequest startWithCompletionHandler:^(FBRequestConnection *connection, id result, NSError *error)
{
NSLog(@"%@",[error description]);
//self.delegate
}];
}
This may be an old thread, but now a days all you have to do is call handleDidBecomeActive in your FBSession object from within (void)applicationDidBecomeActive:(UIApplication *)application in your app delegate.
For example:
- (void)applicationDidBecomeActive:(UIApplication *)application
{
[[[MLSocialNetworksManager sharedManager] MLFacebook] handleDidBecomeActive];
}
Where MLFacebook is my FBSession object.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With