FaceBook登陆API -- Login with API calls
Login with API calls
In this section:
- Overview
- Prerequisites
- Opening a session
- Handling session state changes
- Handling app cold starts
- Testing your login flow
- Additional resources
Overview
This tutorial walks you through how to add Facebook login to your app using your own custom UI for the login and logout controls. This way, you can stay in control of the look and feel of your app and provide a consistent user experience.
本教程教您如何用自定义UI为自己APP应用应用Facebook登陆功能,以及登陆和注销的控制。这样,您就可以看上去感觉是在自己的应用里操作,给用户统一的用户体验。
To implement this login, we will use one of the Facebook SDK core objects: FBSession
. The FBSession
object is used to authenticate a user and manage the user's session. Opening a session will initiate the authentication flow after which a valid user session should be available and subsequently cached in the form of an access token. Cached access tokens, when still valid, can then be used to reopen a session any time. Closing a session logs the user out and can optionally clear the cached token. While an app can create instances of FBSession and manage them itself, FBSession provides helpers to simplify the common scenario of an app having a single logged-in person. We'll use those helpers here. If you want to learn more about how Facebook sessions work, you can read our understanding sessions section.
实现本登陆,我们会用到FacebookSDK 的核心对象之一:FBSession. FBSession 对象被用于认证一个用户并且管理用户的会话。打开一个会话将在有一个有效的用户会话是可用的后初始化认证流程,并且随后以访问令牌的形式存储。存储过的令牌,当他仍是有效的,可以随时重新开始一个会话。关闭一个会话,用户退出并且用户可以选择性的清除已保存的令牌。当一个应用可以创建FBSession的实例并管理它的时候,FBSession对具有单一登陆用户的应用提供简化常见情况的帮助。我们可以利用这里的帮助。如果你想了解更多关于Facebook会话如何工作的,你可以阅读我们的理解会话章节。
This tutorial shows a simple use case where the user is presented with a custom login/logout button that the user can touch to toggle their authentication state. When the user is logged in, we will show then a logged-in UI that consists on a welcome message and changing the button title to "Log out", when the user is logged out we will show them a logout confirmation message and a "Log in with Facebook" button, like this:
本教程展示了一个简单的使用例子,用户使用一个自定义登陆/注销按钮,可以点击切换他们的认证状态 ,当用户登陆时,我们将展示登陆进来的UI,由一个欢迎信息并且改变按钮标题为“Log out”,当用户退出我们将展示一个退出确认信息框并且按钮变成"Log in with Facebook" 像下面这样
We will also check for a cached session when the user opens the app, in order to log the user in automatically if possible. The sample code shown belongs to the FBLoginCustomUISample app. If you want to see the complete code of this sample, you can find it on github.com/fbsamples/ios-howtos. However, this tutorial is self-contained and you don't need to check out the app code to be able to follow it.
当用户打开app时,我们也可以检查存储的会话,为了在可能的情况下自动登陆。这个被展示的示例代码
属于the FBLoginCustomUISample app,如果你想看本例完成的代码,你可以在github.com/fbsamples/ios-howtos.这个地址找到它,然而,本教程是独立的并且你不需要检出代码就能够依照他
Prerequisites
Before you start this tutorial you will need:
- Your environment set up
- A Facebook app properly configured and linked to your iOS app, with login enabled
- The Facebook SDK added to your project
- Your Facebook app ID and display name added your app's .plist file
If you haven't done this and need help doing so, you can follow our getting started guide.
Opening a session
When a user first logs into your app using Facebook, your app passes control to the Facebook iOS app or Facebook in a mobile browser, which will show the user a dialog prompting them to authorize your app. If all goes well, this will result in the creation of a session that will be stored in an access token and cached. However, when you're trying to get your users to log into your app, either with Facebook or otherwise, it is always good practice to minimize the friction by avoiding unnecessary steps. So, if a person has previously logged into your app with Facebook and there's still a valid cached token from that session, we recommend that you open a session using it. This way you eliminate the need for your user to touch a button to log in.
To implement this, in app delegate's application:didFinishLaunchingWithOptions:
method, we will add the following:
-
// 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];
-
}];
We first check if there's a cached token by examining the active session's state. If there is a cached token the session state will be FBSessionStateCreatedTokenLoaded
. If this is the case, we can try opening the cached session using the FBSession
openActiveSessionWithReadPermissions:allowLoginUI:completionHandler:
method, with allowLoginUI:
set to NO
(this will prevent the login dialog from showing). The other arguments that need to be passed to this method are:
- The permissions you're requesting.
- The completion handler, which will be called whenever there's a session state change.
When someone connects with an app using Facebook login, the app can access their public profile and friend list, the pieces of information that are visible to everyone. To create this basic connection, apps must always request access to a person's basic profile information by asking for the basic_info
permission. Similarly, all other pieces of information that someone adds to their Facebook profile are secured behind other read permissions. Apps also need publish permissions in order to post content on the user's behalf. If you want to know more about permissions, you can check out our permissions section.
The openActiveSessionWithReadPermissions:allowLoginUI:completionHandler:
accepts a handler that will be called whenever there's a session state change. This means that your handler method will be called for everyintermediate session state change during login, and not only after the session is open (or opening fails). But also for every other session state change that occurs during the session's entire lifetime. If you want to read more about the FBSession
lifecycle, you can refer to our understanding sessions guide. We will take a closer look at this in thehandling session state changes section of this tutorial.
So far, we have discussed how to open a session if there is a cached token. Opening a session without one is very similar. In our example, we have a single button that the user can touch to toggle authentication. When the user touches this button the app will pass control to the Facebook iOS app or Facebook in a mobile browser, which will show the user a dialog prompting them to authorize the app, like this:
To implement this we need to do three things:
- Add the session opening/closing calls to the login button
- Handle the incoming URL when the Facebook app or Facebook website return control to your app
- Handle the case when the user leaves your app while the Facebook login dialog is being shown
To wire up the button, in the button delegate, we do the following:
-
- (IBAction)buttonTouched:(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) {
-
-
// 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"]
-
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];
-
}];
-
}
-
}
When the user touches the button, if the current session is open, we will close it. Alternatively, if the current session in any state other than open, we will attempt to open it. To check if the session is open, we check if the session state is any of the two open states: FBSessionStateOpen
or FBSessionStateOpenTokenExtended
. If this is the case, we close the session by calling closeAndClearTokenInformation
on the active session. Closing the session will call the session state change handler automatically. If the session is not open, we will attempt to open it by calling the FBSession openActiveSessionWithReadPermissions:allowLoginUI:completionHandler:
method, with allowLoginUI:
set to YES
. Again, we need to specify the permissions (at least basic_info
) and the completion handler. For the completion handler we are using the same method we used for our silent login.
Next, to handle the incoming URL when the Facebook app or Facebook website return control to your app, overrideapplication:openURL:sourceApplication:annotation
in your app delegate to call the FBsession object that handles the incoming URL:
-
// During the Facebook login flow, your app passes control to the Facebook iOS app or Facebook in a mobile browser.
-
// After authentication, your app will be called back with the session information.
-
- (BOOL)application:(UIApplication *)application
-
openURL:(NSURL *)url
-
sourceApplication:(NSString *)sourceApplication
-
annotation:(id)annotation
-
{
-
return [FBAppCall handleOpenURL:url sourceApplication:sourceApplication];
-
}
By default, the FBAppCall
will complete the flow for FBSession.activeSession
. If you are explicitly managingFBSession
instances, use the overload that takes an FBSession
instance.
And finally, you will need to handle the case when the user leaves your app while the Facebook login dialog is being shown. This can happen, for example, if the uses presses the iOS "home" button while the dialog is active. In order to handle this case, override the applicationDidBecomeActive:
method in your app delegate to callFBAppCall handleDidBecomeActive
. This method will check for any session opening processes that might have been left hanging and will clear the unresolved session so you can start anew.
-
- (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];
-
}
In the next session we will see how to handle the session state changes.
Handling session state changes
In the previous session, when we opened a session, we had to provide a handler that will be called whenever there's a session state change. This handler method will be called for every intermediate session state change during login, and not only after the session is open (or opening fails). But also for every other session state change that occurs during the session's entire lifetime. If you want to read more about the FBSession
lifecycle, you can refer to our understanding sessions section.
Here is an example of handler that deals with the different possible state changes and errors:
-
// 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
-
[self userLoggedIn];
-
return;
-
}
-
if (state == FBSessionStateClosed || state == FBSessionStateClosedLoginFailed){
-
// If the session is closed
-
NSLog(@"Session 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
-
-
} 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];
-
}
-
}
In this case, if the session is opened successfully, we will show the user a logged-in UI. If the session is closed, either deliberately or if the login fails, we will show the user the logged-out UI.
If there are any errors we will handle them the following way. First we will call the FBErrorUtility shouldNotifyUserForError:
method passing it the error we got, in order to find out if the error requires people using your app to make an action outside of your app in order to recover. This can be the case, for example, if the user hasn't verified their Facebook account and they have to complete that process before they can use this account to log into your app. In cases like this, the SDK provides an error message with instructions that you can show your user. So, if shouldNotifyUserForError:
returns true
, then we will notify the user by showing them the message we get from the FBErrorUtility
by calling userMessageForError:error
. If not, we will have to handle the error within the app's error logic. To handle the error, we will need more information about it. This information is available in the error category, to retrieve it we can call [FBErrorUtility errorCategoryForError:error]
.
In the case where the user touches the "cancel" when shown the Facebook login dialog, the error category will beFBErrorCategoryUserCancelled
. In this example, we have chosen to ignore this, but you can also choose to show the user a message if you feel that cancelling login will result in the user not being able to complete a task they had initiated in your app (like accessing FB-stored information or posting to Facebook).
We have mentioned that this handler method will be called for every session state change during the entire lifetime of the session, so we also need to handle session closures that can happen outside of your app, even if they are unrelated to the login process. An example of this would be a session closure that happens because the user removed your app from the Facebook UI. For these cases, the error category will beFBErrorCategoryAuthenticationReopenSession
. To recover from this error, we tell our users to log in again.
In this example, we only specifically handle two error categories, providing a generic message for the rest, but you may need to handle other cases depending on your app. You can find more information on the different error categories and what they mean in our FBError
reference. You can also read more about error handling in our error handling guide.
Finally, if there has been any error, we will clear the cached token by calling closeAndClearTokenInformation
on the active session, so next time we attempt a login we start with a fresh session.
Handling app cold starts
Since most login flows require an app switch to complete, it's possible your app gets terminated by iOS in low memory conditions (or if your app does not support backgrounding). In that case, the state change handler block supplied to your open call is lost. To handle that scenario, you can explicitly assign a state change handler block to the FBSession instance any time prior to the handleOpenURL:
call. For example,
-
// During the Facebook login flow, your app passes control to the Facebook iOS app or Facebook in a mobile browser.
-
// After authentication, your app will be called back with the session information.
-
- (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];
-
}