To add a Facebook login button into your app, it can be as easy as using FBLoginButton
which included in the Facebook SDK. However, not much customization can be made on the appearance of FBLoginButton
.
If you need a Facebook login button that matches your app’s look and feel, you will have to create your own custom Facebook Login Button. Furthermore, you also need to implement all the necessary API calls all by yourself.
In this article, I will go through how you can add the Facebook login functionality to your custom button. After that, we will look into how you can make your custom Facebook login button reusable in your other Xcode projects.
Note that I will not dive into how to setup an Xcode project to use Facebook SDK. If you have not properly setup your Xcode project, I recommend you to go through the setup process by referring to my previous article before proceeding with this article.
🔗 Step-by-Step: Facebook Login Integration in Swift
The Overview
For demo purposes, I will create a custom login button that shows text with emoji, implement all necessary login / logout functionalities and shows different title message based on current login status.
Following animated GIF showcases what we will achieve in this article.
With all that being said, let’s fire up your Xcode and get started. 🔨
Adding Custom Facebook Login Button
Before we start working on the custom login button, let’s import FBSDKLoginKit
to you view controller.
import FBSDKLoginKit
We will be using storyboard for our UI implementation.
First, add a button and label into your view controller. Create 2 IBOutlets loginButton
and messageLabel
and connect them to the label and button you just added.
Next, add a “touch up inside” IBAction
named loginButtonTapped(_:)
and connect it to the loginButton
.
After that, we will need 2 private functions which in charge of updating the loginButton
and messageLabel
text based on current login status.
extension CustomButtonViewController {
private func updateButton(isLoggedIn: Bool) {
// 1
let title = isLoggedIn ? "Log out 👋🏻" : "Log in 👍🏻"
loginButton.setTitle(title, for: .normal)
}
private func updateMessage(with name: String?) {
// 2
guard let name = name else {
// User already logged out
messageLabel.text = "Please log in with Facebook."
return
}
// User already logged in
messageLabel.text = "Hello, \(name)!"
}
}
- For the custom login button’s text, show “Log in 👍🏻” when user is logged out, else show “Log out 👋🏻”.
- For title message, show “Please log in with Facebook.” when user is logged out, else show greeting message with user’s name.
In order for both loginButton
and messageLabel
to show the correct information when we launch the screen, we need to call both of these functions in viewDidLoad()
.
override func viewDidLoad() {
super.viewDidLoad()
// 1
updateButton(isLoggedIn: (AccessToken.current != nil))
// 2
updateMessage(with: Profile.current?.name)
}
- Use the existence of access token to determine current login status and pass it into the
updateButton(isLoggedIn:)
function. WhenAccessToken.current
isnil
, means that the user is currently logged out. - Retrieve user’s name from Facebook SDK’s
Profile
class’s static variable and pass it intoupdateMessage(with:)
function to show title message accordingly.
Lastly, we will need to implement the core functionality of this view controller — login and logout. Following is the implementation of loginButtonTapped(_:)
IBAction
.
@IBAction func loginButtonTapped(_ sender: Any) {
// 1
let loginManager = LoginManager()
if let _ = AccessToken.current {
// Access token available -- user already logged in
// Perform log out
// 2
loginManager.logOut()
updateButton(isLoggedIn: false)
updateMessage(with: nil)
} else {
// Access token not available -- user already logged out
// Perform log in
// 3
loginManager.logIn(permissions: [], from: self) { [weak self] (result, error) in
// 4
// Check for error
guard error == nil else {
// Error occurred
print(error!.localizedDescription)
return
}
// 5
// Check for cancel
guard let result = result, !result.isCancelled else {
print("User cancelled login")
return
}
// Successfully logged in
// 6
self?.updateButton(isLoggedIn: true)
// 7
Profile.loadCurrentProfile { (profile, error) in
self?.updateMessage(with: Profile.current?.name)
}
}
}
}
- Create an instance of Facebook SDK’s
LoginManager
class to handle login and logout operations. - When access token is available, perform logout operation and update the text of both
loginButton
andmessageLabel
. - Access token not available, thus perform login operation. Note that the
permissions
parameter is not required when we only accessing basic Facebook profile information. Therefore, we only need to pass in an empty array. - Check for error after login operation completed. If error occurred, we will print out the error and return.
- Check if user cancelled the login operation. If so, nothing else needs to be done, thus just return.
- Successfully logged in, thus update the text of the custom login button.
- Use the
Profile
class’sloadCurrentProfile(completion:)
method to retrieve the logged in user’s Facebook profile information and update the title message.
After finish implementing loginButtonTapped(_:)
, it is time to test out the login and logout operations.
Since your have not make your Facebook app available in production, you will need a test user to test out the login and logout operations.
Check out the “Test Facebook Login Integration Using Test Users” section of Step-by-Step: Facebook Login Integration in Swift to learn how you can setup a test user.
Once you have created a test user, build and run your app. If you followed all the implementation correctly, you should be able to perform login and logout without any problem.
Making Custom Facebook Login Button Reusable
What differentiates great developers from good developers, is that great developers will make their code as modular as possible, so that it can be reused in other projects.
For our case, the custom Facebook login button is a very good candidate for refactoring in order to make it reusable.
Following are what we are trying to achieve from the refactoring:
- Create a
UIButton
subclass. - Define login and logout completion handlers.
- Encapsulate login and logout logic within the login button.
Create a UIButton Subclass
Let’s go ahead and create a UIButton
subclass, name it “MYFacebookLoginButton” and make sure to import FBSDKLoginKit
into MYFacebookLoginButton
.
First, add the updateButton(isLoggedIn:)
method that we previously implemented as a private function. Also, define a commonSetup()
method that we will be implementing shortly.
extension MYFacebookLoginButton {
private func commonSetup() {
}
private func updateButton(isLoggedIn: Bool) {
let title = isLoggedIn ? "Log out 👋🏻" : "Log in 👍🏻"
setTitle(title, for: .normal)
}
}
Next we will need to implement 2 init
methods for the MYFacebookLoginButton
.
init(frame:)
→ Required for initialization using code.init?(coder:)
→ Required for initialization using storyboard.
override public init(frame: CGRect) {
super.init(frame: frame)
commonSetup()
}
required public init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonSetup()
}
As you can see, we will call commonSetup()
every time MYFacebookLoginButton
being initialized to perform any necessary configuration.
Here’s the implementation of commonSetup()
.
private func commonSetup() {
// 1
// Set button title
updateButton(isLoggedIn: (AccessToken.current != nil))
// 2
responsibleViewController = findResponsibleViewController()
// 3
// Override touch up inside event
addTarget(self, action: #selector(touchUpInside(sender:)), for: .touchUpInside)
}
- Update
MYFacebookLoginButton
‘s text based on current login status. - Use
findResponsibleViewController()
to get reference of view controller that responsible to (own) theMYFacebookLoginButton
instance, we will need the responsible view controller when we implement the login logic later. Do note that this method is not yet implemented, we will get into that later. - Add
MYFacebookLoginButton
as target to handle its “touch up inside” event.
You might notice that we have not defined the responsibleViewController
variable. Let’s go ahead and define it. Note that we will define it as weak
variable to avoid retain cycle.
private weak var responsibleViewController: UIViewController!
Next up, let’s look into the implementation of findResponsibleViewController()
method.
extension UIView {
/// Find the view controller that responsible for a particular view
func findResponsibleViewController() -> UIViewController? {
if let nextResponder = self.next as? UIViewController {
return nextResponder
} else if let nextResponder = self.next as? UIView {
return nextResponder.findResponsibleViewController()
} else {
return nil
}
}
}
As shown in the code snippet above, it is a UIView
extension that recursively go through a view’s stack to look for its responsible view controller. For more information, you can refer to this article.
Since UIButton
is a subclass of UIView
, thus we can utilize the findResponsibleViewController()
method in our case.
Define Login and Logout Completion Handlers
We will define 2 closures as handler for both login and logout completion. Both of these handlers will trigger after user successfully login or logout using Facebook.
var loginCompletionHandler: ((MYFacebookLoginButton, Result<LoginManagerLoginResult, Error>) -> Void)?
var logoutCompletionHandler: ((MYFacebookLoginButton) -> Void)?
Passing the button itself to both handlers is generally a good practice, this can avoid accidental retain cycles if the button is used within one of its own completion handlers and we forget to capture it weakly.
For login completion handler, there will be another extra parameter, which is the login result return by Facebook SDK. Here we will be using Result
type introduced in Swift 5 to pass back either an instance of LoginManagerLoginResult
or an instance of Error
.
If you want to know more about Result type, check out this article.
Encapsulate Login and Logout Logic Within the Login Button
To encapsulate the login and logout logic within the login button, we will implement the “touch up inside” event handler within MYFacebookLoginButton
. The logic is very similar to the loginButtonTapped(_:)
IBAction
that we previously implemented.
@objc private func touchUpInside(sender: MYFacebookLoginButton) {
let loginManager = LoginManager()
if let _ = AccessToken.current {
// Access token available -- user already logged in
// Perform log out
loginManager.logOut()
updateButton(isLoggedIn: false)
// 1
// Trigger logout completed handler
logoutCompletionHandler?(self)
} else {
// Access token not available -- user already logged out
// Perform log in
// 2
loginManager.logIn(permissions: [], from: responsibleViewController) { [weak self] (result, error) in
// 3
// Check for error
guard error == nil else {
// Error occurred
print(error!.localizedDescription)
if let self = self {
self.loginCompletionHandler?(self, .failure(error!))
}
return
}
// Check for cancel
guard let result = result, !result.isCancelled else {
print("User cancelled login")
return
}
// Successfully logged in
self?.updateButton(isLoggedIn: true)
// 4
// Trigger login completed handler
if let self = self {
self.loginCompletionHandler?(self, .success(result))
}
}
}
}
There are a few places that we need to take note here:
- Trigger the logout completion handler after logout completed.
- Pass in
responsibleViewController
when callinglogin()
method ofloginManager
. - Trigger the login completion handler after login completed with error. There are 2 things to take note here. Since we are capturing
self
weakly within theloginManager
‘s completion handler, thus we will use optional binding to check the availability ofself
. Another thing to take note is that we are passing the error object as thefailure
case ofResult
type. - Trigger the login completion handler after login completed successfully. Here, we pass the result object as the
success
case ofResult
type.
With that, we have completed the implementation of the reusable custom Facebook login button.
Using the Reusable Custom Facebook Login Button
There are 2 ways you can add the reusable button to your view controller:
- Using code (programatically)
- Using storyboard
To programatically add the button to your view controller, paste below code snippet to your view controller’s viewDidLoad()
method.
let fbButton = MYFacebookLoginButton(frame: CGRect(x: 10, y: 200, width: 100, height: 50))
fbButton.backgroundColor = .black
view.addSubview(fbButton)
If you prefer to use storyboard, just add a UIButton
to your view controller and set its class to MYFacebookLoginButton
. Then make an IBOutlet
connection to your view controller.
Lastly, let’s look at how to use the login and logout completion handler to update the messageLabel
. Add the following code to your view controller’s viewDidLoad()
method.
// Implement login completion handler
loginButton.loginCompletionHandler = { [weak self] (button, result) in
switch result {
case .success(let result):
print("Access token: \(String(describing: result.token?.tokenString))")
// 1
// Show message after login completed
Profile.loadCurrentProfile { (profile, error) in
self?.updateMessage(with: Profile.current?.name)
}
case .failure(let error):
// 2
print("Error occurred: \(error.localizedDescription)")
break
}
}
// Implement logout completion handler
loginButton.logoutCompletionHandler = { [weak self] (button) in
// 3
// Show message after logout completed
self?.updateMessage(with: nil)
}
- Handle
success
case of theResult
type. Use theProfile
class’sloadCurrentProfile(completion:)
method to retrieve the logged in user’s Facebook profile information and update the title message. - Handle
failure
case of theResult
type. - Update the title message after user successfully logged out.
Note that the updateMessage(with:)
method that being called here is exactly the same as the one we previously implemented.
That’s about it! You can now build and run your app to see the reusable custom Facebook login button in action. 🤟🏻
Wrapping Up
We have only implemented the basic login and logout functionalities for our reusable Facebook login button, you can most definitely improve it by extending its functionalities.
Feel free to download the full sample project on Github and add in your desired functionalities.
Further Reading
- Step-by-Step: Facebook Login Integration in Swift
- The power of Result types in Swift
- How to find the view controller responsible for a view
I hope you find this article helpful. If you like this article, feel free to share it with your friends and leave me a comment.
You can follow me on Twitter for more article related to iOS development.
Thanks for reading! 👨🏼💻
👋🏻 Hey!
While you’re still here, why not check out some of my favorite Mac tools on Setapp? They will definitely help improve your day-to-day productivity. Additionally, doing so will also help support my work.
- ✨ Bartender: Superpower your menu bar and take full control over your menu bar items.
- ✨ CleanShot X: The best screen capture app I’ve ever used.
- ✨ PixelSnap: Measure on-screen elements with ease and precision.
- ✨ iStat Menus: Track CPU, GPU, sensors, and more, all in one convenient tool.