FacebookSDK(4.1.x) Custom Login UI Button - Swift(1.2)

13,528

Solution 1

Login with custom button and access token.

Get user info in facebook sdk 4.x

Swift

@IBAction func btnFBLoginPressed(sender: AnyObject) {
    var fbLoginManager : FBSDKLoginManager = FBSDKLoginManager()
    fbLoginManager.logInWithReadPermissions(["email"], fromViewController: self, handler: { (result, error) -> Void in
        if (error == nil){
            var fbloginresult : FBSDKLoginManagerLoginResult = result
            if(fbloginresult.grantedPermissions.contains("email"))
            {
                self.getFBUserData()
                fbLoginManager.logOut()
            }
        }
    })
}

func getFBUserData(){
    if((FBSDKAccessToken.currentAccessToken()) != nil){
        FBSDKGraphRequest(graphPath: "me", parameters: ["fields": "id, name, first_name, last_name, picture.type(large), email"]).startWithCompletionHandler({ (connection, result, error) -> Void in
            if (error == nil){
                println(result)
            }
        })
    }
}

Solution 2

Updated answer using the last Facebook SDK (01/05/2016) and Swift 2.1

Create a UIButton with Interface Builder or by code and link the action of that button with this:

@IBAction func loginFacebookAction(sender: AnyObject) {
    let fbLoginManager : FBSDKLoginManager = FBSDKLoginManager()
    fbLoginManager.logInWithReadPermissions(["email"], fromViewController: self) { (result, error) -> Void in
        if (error == nil){
            let fbloginresult : FBSDKLoginManagerLoginResult = result
            if(fbloginresult.grantedPermissions.contains("email"))
            {
                self.getFBUserData()
            }
        }
    }
}

The happy case of the previous code triggers the function self.getFBUserData() so you have to implement that function in the same file

func getFBUserData(){
    if((FBSDKAccessToken.currentAccessToken()) != nil){
        FBSDKGraphRequest(graphPath: "me", parameters: ["fields": "id, name, first_name, last_name, picture.type(large), email"]).startWithCompletionHandler({ (connection, result, error) -> Void in
            if (error == nil){
                //everything works print the user data
                print(result)
            }
        })
    }
}

Solution 3

I did it successfully in xcode 7.3

    @IBAction func fbLoginBtnAction(sender: AnyObject) {

    let fbLoginManager : FBSDKLoginManager = FBSDKLoginManager()

    fbLoginManager.logInWithReadPermissions(["email"], fromViewController: self, handler: {(result, error) -> Void in
            if error == nil {
                print("Logged in through facebook" )
                self.getFBUserData()
            }
            else {
                print("Facebook Login Error----\n",error)
            }
        }
    )
}


  func getFBUserData () {
    if((FBSDKAccessToken.currentAccessToken()) != nil){
        FBSDKGraphRequest(graphPath: "me", parameters: ["fields": "id, name, first_name, last_name, picture.type(large), email"]).startWithCompletionHandler({ (connection, result, error) -> Void in
            if (error == nil){
                print(result)
            }
        })
    }
}

Solution 4

Reusable class (Swift 4).

Usage: FacebookSignIn.shared.signIn(from: yourVC, completion: yourCompletion)

class FacebookSignIn {

   enum Error: Swift.Error {
      case unableToInitializeGraphRequest
      case unexpectedGraphResponse
      case permissionsIsNotGranted
      case unexpectedLoginResponse
      case canceled
   }

   struct Permissions {

      static let email = "email"
      static let profile = "public_profile"

      static func isValidPermissions(_ permissions: Set<AnyHashable>) -> Bool {
         return permissions.contains(email) && permissions.contains(profile)
      }

      static var permissions: [String] {
         return [email, profile]
      }
   }

   public static let shared = FacebookSignIn()

   private init() {
   }

}

extension FacebookSignIn {

   func signIn(from: UIViewController, completion: Result<SignInResponse>.Completion?) {
      let manager = FBSDKLoginManager()
      manager.loginBehavior = .native
      if !isValidToken {
         manager.logOut()
      }
      if let token = FBSDKAccessToken.current() {
         let interval = token.expirationDate.timeIntervalSince(Date())
         if interval > 300 { // At least 5 min token will be valid
            performLogin {
               switch $0 {
               case .failure(let error):
                  completion?(.failure(error))
               case .success(let info):
                  completion?(.success(SignInResponse(accessToken: token.tokenString, userInfo: info)))
               }
            }
         } else {
            FBSDKAccessToken.refreshCurrentAccessToken { [weak self] _, _, error in
               if let error = error {
                  manager.logOut()
                  completion?(.failure(error))
               } else {
                  let token = FBSDKAccessToken.current()?.tokenString ?? "" // Should be always valid value at this point.
                  self?.performLogin {
                     switch $0 {
                     case .failure(let error):
                        completion?(.failure(error))
                     case .success(let info):
                        completion?(.success(SignInResponse(accessToken: token, userInfo: info)))
                     }
                  }
               }
            }
         }
      } else {
         manager.logIn(withReadPermissions: Permissions.permissions, from: from) { [weak self] result, error in
            if let error = error {
               manager.logOut()
               completion?(.failure(error))
               return
            }
            guard let result = result else {
               manager.logOut()
               completion?(.failure(Error.unexpectedLoginResponse))
               return
            }
            let permissions = result.grantedPermissions ?? Set<AnyHashable>()
            let token = result.token?.tokenString ?? "" // Should be always valid value at this point.
            if result.isCancelled {
               manager.logOut()
               completion?(.failure(Error.canceled))
            } else if Permissions.isValidPermissions(permissions) {
               self?.performLogin {
                  switch $0 {
                  case .failure(let error):
                     completion?(.failure(error))
                  case .success(let info):
                     completion?(.success(SignInResponse(accessToken: token, userInfo: info)))
                  }
               }
            } else {
               manager.logOut()
               completion?(.failure(Error.permissionsIsNotGranted))
            }
         }
      }
   }

   private var isValidToken: Bool {
      guard let token = FBSDKAccessToken.current() else {
         return false
      }
      return Permissions.isValidPermissions(token.permissions ?? Set<AnyHashable>())
   }

   private func makeGraphRequest() -> FBSDKGraphRequest? {
      guard FBSDKAccessToken.current().tokenString != nil else {
         return nil
      }
      // You might not get email: https://developers.facebook.com/docs/facebook-login/permissions/v2.4
      // Note, even if you request the email permission it is not guaranteed you will get an email address. For example,
      // if someone signed up for Facebook with a phone number instead of an email address, the email field may be empty.
      let fields = "email,id,first_name,last_name,gender"
      return FBSDKGraphRequest(graphPath: "me", parameters: ["fields": fields])
   }

   private func performLogin(completion: Result<[String: String]>.Completion?) {
      let manager = FBSDKLoginManager()
      guard let request = makeGraphRequest() else {
         manager.logOut()
         completion?(.failure(Error.unableToInitializeGraphRequest))
         return
      }
      _ = request.start { _, result, error in
         if let e = error {
            manager.logOut()
            completion?(.failure(e))
         } else if let result = result as? [String: String] {
            completion?(.success((result)))
         } else {
            manager.logOut()
            completion?(.failure(Error.unexpectedGraphResponse))
         }
      }
   }

}

public struct SignInResponse {

   public let accessToken: String
   public let userInfo: [String: String]

   public init(accessToken: String, userInfo: [String: String]) {
      self.accessToken = accessToken
      self.userInfo = userInfo
   }
}


public enum Result<T> {

   case success(T)
   case failure(Swift.Error)

   public typealias Completion = (Result<T>) -> Void
}
Share:
13,528
senty
Author by

senty

Harder, Better, Faster, Stronger...

Updated on June 26, 2022

Comments

  • senty
    senty almost 2 years

    Following this tutorial, I have managed to make a Facebook Login Button working. However, it is assigning the button image automatically from the SDK and it is not customisable (as it is not getting created on Storyboard), so I am unable to use my own button image or text instead.

    I believe this part of the code (in ViewDidLoad) is assigning the button:

            let loginView : FBSDKLoginButton = FBSDKLoginButton()
            self.view.addSubview(loginView)
            loginView.center = self.view.center
            loginView.readPermissions = ["public_profile", "email"]
            loginView.delegate = self
    

    What I need to do is creating a @IBOutlet Button on Storyboard and customise it from there. How can I do that?