I'm a beginner in the iOS (native) world, I have a strange issue with the Facebook authentication in my demo app.
I've configured the .plist file with the following keys following the tutorial:
FacebbokAppID: <myappid>
FacebookDisplayName: a name
Url types (array)
Item 0 (dictionary)
Url Schemes (Array)
Item 0 (String): fb<myappid>
Where <myappid> is the numerical app id of the registered app on the facebook website.
Now, I perform the login using this code:
- (void) facebookLoginLogout
{
// 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) {
// Close the session and remove the access token from the cache
// The session state handler (in the app delegate) will be called automatically
[FBSession.activeSession closeAndClearTokenInformation];
// If the session state is not any of the two "open" states when the button is clicked
} else {
// Open a session showing the user the login UI
// You must ALWAYS ask for basic_info permissions when opening a session
[FBSession openActiveSessionWithReadPermissions:#[#"basic_info", #"friends_online_presence"]
allowLoginUI:YES
completionHandler:
^(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];
}];
}
}
And I've defined the method to handle state changes:
- (void)sessionStateChanged:(FBSession *)session state:(FBSessionState) state error:(NSError *)error
{
NSLog(#"here?");
[..]
}
The problem is exactly that this method is never called in this situation.
THE STRANGE POINT is that, if I change Url types -> Item 0 -> Url Schemes -> Item 0 value to something different, the method is called!
Any hints? Thanks in advance.
Related
I have successfully logged user to Facebook using Facebook Graph API and now i need to fetch the user's Facebook page list (page status is already changed to Published).
My code looks like :
(void)checkLoginWithFacebook {
// 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) {
// Close the session and remove the access token from the cache
// The session state handler (in the app delegate) will be called automatically
// If the session state is not any of the two "open" states when the button is clicked
[self getListOfPages];
}
else
{
// Open a session showing the user the login UI
// You must ALWAYS ask for basic_info permissions when opening a session
// This will bypass the ios6 integration since it does not allow for a session to be opened
// with publish only permissions!
FBSession* sess = [[FBSession alloc] initWithPermissions:[NSArray arrayWithObjects:#"publish_actions",nil]];
[FBSession setActiveSession:sess];
[sess openWithBehavior:(FBSessionLoginBehaviorForcingWebView) completionHandler:^(FBSession *session, FBSessionState state, NSError *error)
{
[[AppDelegate appDel] sessionStateChanged:session state:state error:error];
if (state == FBSessionStateClosed || state == FBSessionStateClosedLoginFailed)
{
NSLog(#"session closed");
return ;
}
[self getListOfPages];
}];
}
}
(void)getListOfPages {
[FBRequestConnection startWithGraphPath:#"/me/accounts"
completionHandler:^(
FBRequestConnection *connection,
id result,
NSError *error
) {
/* handle the result */
NSLog(#"pages result: %# ",result);
}];
}
Response:
pages result: {
data = ();
}
Please Advice. Thanks.
Have you requested the manage_pages permission from the respective user through the login dialog? The code you posted look ok IMHO, I think you see an empty result because of the missing permission.
in htttps://developer.facebook.com they have give login with API call Sample, they asked to type following code in my app delegate.m file
// 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:NO
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];
}];
it is showing me the error like ---- No visible #interface for 'AppDelegate' declares the 'sessionStateChanged:state:error:'
thanks in advance...
According to above link, You have to add this method in your app delegate.. But you can customize this method according to your view by state(state == FBSessionStateOpen... etc)
// 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
// customize your code...
}
I have a VC with a button hooked up to do a facebook share of some text and an image url.
I use the FB api which opens the FB webview - for the user to login. It then presents the share window with a "Skip" or "Share" button as shown...
I have in my app the following code to take care of the login and button actions ....
For some reason the "Skip" also posts as if the "OK" was hit. I tried cheking thr error code returned using the Error Utility but that does not work (app never gets into that section of code and "CANCELLED OUT" is never logged in NSLog, even when I hit the Skip button)
Any help with this will be most appreciated. Perhaps some way to recheck permissions?
(void)postStatusUpdate:(id)status
{
if (FBSession.activeSession.state != FBSessionStateCreatedTokenLoaded || [FBSession.activeSession.permissions
indexOfObject:#"publish_actions"] == NSNotFound) {
// Permission hasn't been granted, so ask for publish_actions
[FBSessionopenActiveSessionWithPublishPermissions:#[#"publish_actions"]
defaultAudience:FBSessionDefaultAudienceFriends
allowLoginUI:YES
completionHandler:^(FBSession *session, FBSessionState state,
NSError *error) {
[selfsessionStateChanged:session state:state error:error];
if([FBErrorUtilityerrorCategoryForError:error] ==
FBErrorCategoryUserCancelled)
{
NSLog(#"SNFacebookClient: CANCELLED OUT!!");
}
if (FBSession.activeSession.isOpen && !error) {
// Publish the story if permission was granted
[selfpublishStory:status];
}
}];
} else {
// If permissions present, publish the story
[selfpublishStory:status];
}
}
code here
I even tried in lldb to check the permissions array after "Skip" was pressed ...
po [FBSession activeSession].permissions
And find that even when skip is pressed the array contains the entry "publish_actions"
You need to check the session status, if the status is FBSessionStateOpen or FBSessionStateCreatedTokenLoaded, you can fetch user's data.
Otherwise, something wrong happen or user skipped it.
This approach is different of you, check my short comments to you understand the steps.
// Suppose my FBSession is stored in a property called session
#property (nonatomic, strong) FBSession *session;
// The session getter can be implemented like this
- (FBSession *)session
{
if(!_session)
{
_session = [[FBSession alloc] initWithPermissions:readPermissions];
[FBSession setActiveSession:_session];
}
return _session;
}
// Opening a new session
[self.session openWithCompletionHandler:^(FBSession *session, FBSessionState status, NSError *error) {
[self session:session stateChanged:status error:error];
}];
- (void)session:(FBSession *)session stateChanged:(FBSessionState)state error:(NSError *)error
{
switch(state)
{
case FBSessionStateOpen:
{
// Connection accepted
}
break;
case FBSessionStateCreatedTokenLoaded:
{
// Connection restored
}
break;
case FBSessionStateClosed:
// Connection closed
break;
case FBSessionStateClosedLoginFailed:
{
// Connection failed and closed
}
break;
case FBSessionStateCreated:
{
// Session created (not opened yet)
}
break;
case FBSessionStateCreatedOpening:
// Handler it if you need to do something while connection is opening
break;
case FBSessionStateOpenTokenExtended:
// Session token extended
break;
}
}
I am working on an iOS app using the latest FB SDK for native log in. When I switch my app off in "allow these apps to use your account" in the settings, an error "com.facebook.sdk error 2" is expected to come.
I am wondering is there any elegant way to solve this error even if "allow these apps to use your account" is off for my app? I have searched for the solution but all the answers are saying that You need to switch that option on. But I think the better way is that if user switches that option off, we can still let him log in, falling back to the fast-app-switch way seamlessly, just like he doesn't log into Facebook on his device at all. How can I do this in the newest FB SDK? Thanks!
====================================Update=========================================
I solve it using a deprecated function openActiveSessionWithPermissions:allowLoginUI:completionHandler
first we need to check whether user switch this option off:
self.useAccountAllowed = true;
ACAccountStore *accountStore;
ACAccountType *accountTypeFB;
if ((accountStore = [[ACAccountStore alloc] init]) &&
(accountTypeFB = [accountStore accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierFacebook] ) ){
NSArray *fbAccounts = [accountStore accountsWithAccountType:accountTypeFB];
id account;
if (!fbAccounts)
{
//do not log into FB on the device
}
else if ([fbAccounts count] == 0) {
[FBSession.activeSession closeAndClearTokenInformation];
self.useAccountAllowed = false; //user switch this option off
}
then in openSession function, using that deprecated function if self.useAccountAllowed is false:
if (self.useAccountAllowed) {
[FBSession openActiveSessionWithReadPermissions:nil allowLoginUI:YES completionHandler:^(FBSession* session, FBSessionState status, NSError* error){
[self sessionStateChanged:session state:status error:error];}];
}
else {
NSArray* lPermission = FBSession.activeSession.permissions;
[FBSession openActiveSessionWithPermissions:lPermission allowLoginUI:YES completionHandler:^(FBSession* session, FBSessionState status, NSError* error){
[self sessionStateChanged:session state:status error:error];}];
not sure whether it is a correct way.
This is how I solved it. On the AppDelegate implementation file, in the applicationDidBecomeActive method, use the regular [FBSession.activeSession handleDidBecomeActive] method, as recommended by the FB SDK documentation. Plus, add a new method that checks the user permissions in Settings (that I called checkPermissionSettings in the example below):
- (void)applicationDidBecomeActive:(UIApplication *)application
{
NSLog(#"applicationDidBecomeActive: in NHOCAppDelegate");
//
// The flow back to your app may be interrupted (for ex: if the user clicks the Home button
// while if authenticating via the Facebook for iOS app).
// If this happens, the Facebook SDK can take care of any cleanup that may include starting a fresh session.
//
[FBSession.activeSession handleDidBecomeActive];
[self checkPermissionSettings];
}
//
// Verify if the user pressed the Home Button, went to Settings and deauthorized the app via "Allow These Apps to Use Your Account..."
// If so, redirect him to the login screen (this happens automagically, see below).
//
- (void)checkPermissionSettings
{
NSLog(#"checkPermissionSettings: in NHOCAppDelegate");
//
// Now 'startForMeWithCompletionHandler' may return 'FBSessionStateClosed' (meaning that the user probably unauthorized the app in Settings).
//
// If that is the case:
//
// - Hide the 'logged' View Controller
// - Remove it (NHOCLoggedVC) from the Notification Center
// - Show the 'login' View Controller
// - And finally add it (NHOCLoginVC) to the Notification Center, closing the loop
//
// Check the console for further info.
//
[FBRequestConnection startForMeWithCompletionHandler:^(FBRequestConnection *connection, id<FBGraphUser> user, NSError *error) {
if (!error) {
//
// Everything went fine... The app is in good shape.
// Notice that 'user.location' requires user_location permission
//
NSLog(#"user.location: %#: ", [user.location objectForKey:#"name"]);
}
}];
}
To make it work as designed, I also use Notification Center. You can check the entire example here:
FB SDK + Storyboards with Publish to Feed
I hope it helps.
I implemented the whole iOS Facebook login process in one of my iOS Game.
In the app, you can either log-in with an email account or through Facebook. I occasionally present a view inviting the user to sync his account with Facebook if he logged-in with an email.
Here is the code I'm using to determine wheither or not I should display this view to the user :
if (FBSession.activeSession.isOpen) {
// Present the view
} else {
// Go somewhere else
}
At first it seemed to work great but it turns out that FBSession.activeSession.isOpen returns NO at some point even though the user is logged in with FB.
My first guess would be that the accessToken expires. I am not sure if it's a good clue because I am doing the following in my application:didFinishLaunchingWithOptions: method (like Facebook recommends to):
if (FBSession.activeSession.state == FBSessionStateCreatedTokenLoaded) {
// Yes, so just open the session (this won't display any UX).
[self openSessionWithCompletionBlock:^{}];
}
So now I have no idea how to reproduce or to fix this problem, any idea? Is it possible that the token expires after the app started? Like if the user keeps the app in background for a quiet long time?
It's just a facebook behavior, you need to 'reopen' the session everytime you launch the app, your guess is sort of right, It's because of the token state, a simple solution is that you check the [FBSession activeSession].isOpen state and if it returns NO call the openActiveSessionWithAllowLoginUI << Yes the same facebook method, but checking all the states it returns.
If the session was previously opened then it uses the stored token to reopen the session, you shouldn't worry about the login UI because it won't show up again as the session was previously opened.
if (![FBSession activeSession].isOpen) {
[self connectWithFacebook];
}
- (void) connectWithFacebook {
// The user has initiated a login, so call the openSession method
// and show the login UI if necessary << Only if user has never
// logged in or ir requesting new permissions.
PPAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
[appDelegate openSessionWithAllowLoginUI:YES];
}
And in the app delegate
- (BOOL)openSessionWithAllowLoginUI:(BOOL)allowLoginUI
{
NSArray *permissions = #[#"any_READ_permision_you_may_need"];
return [FBSession openActiveSessionWithReadPermissions:permissions
allowLoginUI:allowLoginUI
completionHandler:^(FBSession *session, FBSessionState state, NSError *error) {
if (error) {
NSLog (#"Handle error %#", error.localizedDescription);
} else {
[self checkSessionState:state];
}
}];
}
- (void) checkSessionState:(FBState)state {
switch (state) {
case FBSessionStateOpen:
break;
case FBSessionStateCreated:
break;
case FBSessionStateCreatedOpening:
break;
case FBSessionStateCreatedTokenLoaded:
break;
case FBSessionStateOpenTokenExtended:
// I think this is the state that is calling
break;
case FBSessionStateClosed:
break;
case FBSessionStateClosedLoginFailed:
break;
default:
break;
}
}
It's a quick fix and it works, you can reproduce it also just by closing the session but without clearing the token cache using [[FBSession activeSession] close] You can even change the token cache policy as described here http://developers.facebook.com/docs/howtos/token-caching-ios-sdk/