Skip to content

Instantly share code, notes, and snippets.

@scottdelly
Last active April 23, 2021 21:43
Show Gist options
  • Star 38 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
  • Save scottdelly/135b35966b1a8de8d2d0 to your computer and use it in GitHub Desktop.
Save scottdelly/135b35966b1a8de8d2d0 to your computer and use it in GitHub Desktop.
Facebook Login with iOS SDK 4.0 in Swift
//If you have a Bridging-Header:
#import <FBSDKCoreKit/FBSDKCoreKit.h>
#import <FBSDKLoginKit/FBSDKLoginKit.h>
//In your AppDelegate:
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [String: AnyObject]?) -> Bool {
//App launch code
FBSDKApplicationDelegate.sharedInstance().application(application, didFinishLaunchingWithOptions: launchOptions)
//Optionally add to ensure your credentials are valid:
FBSDKLoginManager.renewSystemCredentials { (result:ACAccountCredentialRenewResult, error:NSError!) -> Void in
//
}
}
func application(application: UIApplication, openURL url: NSURL, sourceApplication: String?, annotation: AnyObject?) -> Bool {
//Even though the Facebook SDK can make this determinitaion on its own,
//let's make sure that the facebook SDK only sees urls intended for it,
//facebook has enough info already!
let isFacebookURL = url.scheme != nil && url.scheme!.hasPrefix("fb\(FBSDKSettings.appID())") && url.host == "authorize"
if isFacebookURL {
return FBSDKApplicationDelegate.sharedInstance().application(application, openURL: url, sourceApplication: sourceApplication, annotation: annotation)
}
return false
}
func applicationDidBecomeActive(application: UIApplication) {
//App activation code
FBSDKAppEvents.activateApp()
}
//Here's the facebook login code, have your login procedure call this:
let facebookReadPermissions = ["public_profile", "email", "user_friends"]
//Some other options: "user_about_me", "user_birthday", "user_hometown", "user_likes", "user_interests", "user_photos", "friends_photos", "friends_hometown", "friends_location", "friends_education_history"
func loginToFacebookWithSuccess(successBlock: () -> (), andFailure failureBlock: (NSError?) -> ()) {
if FBSDKAccessToken.currentAccessToken() != nil {
//For debugging, when we want to ensure that facebook login always happens
//FBSDKLoginManager().logOut()
//Otherwise do:
return
}
FBSDKLoginManager().logInWithReadPermissions(self.facebookReadPermissions, handler: { (result:FBSDKLoginManagerLoginResult!, error:NSError!) -> Void in
if error != nil {
//According to Facebook:
//Errors will rarely occur in the typical login flow because the login dialog
//presented by Facebook via single sign on will guide the users to resolve any errors.
// Process error
FBSDKLoginManager().logOut()
failureBlock(error)
} else if result.isCancelled {
// Handle cancellations
FBSDKLoginManager().logOut()
failureBlock(nil)
} else {
// If you ask for multiple permissions at once, you
// should check if specific permissions missing
var allPermsGranted = true
//result.grantedPermissions returns an array of _NSCFString pointers
let grantedPermissions = result.grantedPermissions.allObjects.map( {"\($0)"} )
for permission in self.facebookReadPermissions {
if !contains(grantedPermissions, permission) {
allPermsGranted = false
break
}
}
if allPermsGranted {
// Do work
let fbToken = result.token.tokenString
let fbUserID = resutl.token.userID
//Send fbToken and fbUserID to your web API for processing, or just hang on to that locally if needed
//self.post("myserver/myendpoint", parameters: ["token": fbToken, "userID": fbUserId]) {(error: NSError?) ->() in
// if error != nil {
// failureBlock(error)
// } else {
// successBlock(maybeSomeInfoHere?)
// }
//}
successBlock()
} else {
//The user did not grant all permissions requested
//Discover which permissions are granted
//and if you can live without the declined ones
failureBlock((nil)
}
}
})
}
@KOWLOR
Copy link

KOWLOR commented Apr 9, 2015

Great ! Really useful !
There are 2 errors 😊

Lines :
75 : resutl instead of result
92 : (( instead of (

@gonelf
Copy link

gonelf commented May 26, 2015

line 65: let grantedPermissions = result.grantedPermissions.allObjects.map( {"($0)"} )
this will not work on Swift 1.2 since the object grantedPermissions object is a Set and not NSSet

either you cast it to NSSet
ex:
let perms = result.grantedPermissions as NSSet
let grantedPermissions = perms.allObjects.map({"($0)"})

or add map to the Set object
ex:
extension Set {
func map(transform: (T) -> U) -> Set {
return Set(Swift.map(self, transform))
}
}

@UtkuDalmaz
Copy link

What if we need to get publish permissions ?

@tobbe01
Copy link

tobbe01 commented Jun 2, 2015

Hi there! Im haveing some trouble with the
let perms = result.grantedPermissions as NSSet
let grantedPermissions = perms.allObjects.map({"($0)"})

Getting the error
Cannot invoke 'map' with an argument list of type '(() -> _)'

Any chance for some guidance? :)

@Aashays-whichit
Copy link

Hi this is what i've been searching for! I was just wondering if there was an Objective-C version of this?

@minikin
Copy link

minikin commented Jun 24, 2015

thank you for the tips!

@dolzhenko-tony
Copy link

tobbe01. try replace this:

let grantedPermissions = perms.allObjects.map({"($0)"})

to:

let grantedPermissions = Array(result.grantedPermissions).map( {"\($0)"} )

@ltfschoen
Copy link

Using your code with Xcode 7 Beta I encountered error on Line 20, so I changed Line 20 to 24 to the following nested try/catch blocks since Swift 2 "no longer returns Bool and is instead annotated throws":

        if url.scheme != "" {
            do {
                try url.scheme.hasPrefix("fb\(FBSDKSettings.appID())")
                do {
                    try url.host == "authorize"
                    return FBSDKApplicationDelegate.sharedInstance().application(application, openURL: url, sourceApplication: sourceApplication, annotation: annotation)
                } catch let error as NSError {
                    NSLog("Unresolved error \(error), \(error.userInfo)")
                    // Handle Error
                    return false
                }
            } catch let error as NSError {
                NSLog("Unresolved error \(error), \(error.userInfo)")
                // Handle Error
                return false
            }
        }

@cworsley4
Copy link

Could you call declinedPermissions instead of looping through the granted ones to detected if the user denied anything? Then you avoid the loop.

@kostapappas
Copy link

change from
FBSDKLoginManager().logInWithReadPermissions(self.facebookReadPermissions, handler: { (result:FBSDKLoginManagerLoginResult!, error:NSError!) -> Void in
to
FBSDKLoginManager().logInWithReadPermissions(self.facebookReadPermissions, fromViewController: viewController,handler: { (result:FBSDKLoginManagerLoginResult!, error:NSError!) -> Void in

because is deprecated

@WaterfallDeveloper
Copy link

thank you for a very valuable post and discussion; took me some time to make it work for Swift 2.2 though, so here is my version. Yes, you might want to make it more pretty with guard statements etc. but I just needed this to work for now...

AppDelegate.swift excerpt

// according to SDK documentation, this is now possible, check https://developers.facebook.com/docs/ios/getting-started/advanced#swift
import FBSDKCoreKit
import FBSDKShareKit
import FBSDKLoginKit

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?


    func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
        // Override point for customization after application launch.

        //App launch code
        FBSDKApplicationDelegate.sharedInstance().application(application, didFinishLaunchingWithOptions: launchOptions)
        //Optionally add to ensure your credentials are valid:
        FBSDKLoginManager.renewSystemCredentials { (result:ACAccountCredentialRenewResult, error:NSError!) -> Void in
            //
        }

        return true
    }

    func application(application: UIApplication, openURL url: NSURL, sourceApplication: String?, annotation: AnyObject) -> Bool {
        //Even though the Facebook SDK can make this determinitaion on its own,
        //let's make sure that the facebook SDK only sees urls intended for it,
        //facebook has enough info already!
        guard url.scheme.hasPrefix("fb\(FBSDKSettings.appID())") && url.host == "authorize" else {
            return false
        }

        return FBSDKApplicationDelegate.sharedInstance().application(application, openURL: url, sourceApplication: sourceApplication, annotation: annotation)

    }

    func applicationDidBecomeActive(application: UIApplication) {
        // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.

        //App activation code
        FBSDKAppEvents.activateApp()
    }
// . . .
}

FacebookLoginClient.swift


import Foundation

// according to SDK documentation, this is now possible, check https://developers.facebook.com/docs/ios/getting-started/advanced#swift
import FBSDKCoreKit
import FBSDKShareKit
import FBSDKLoginKit


let facebookReadPermissions = ["public_profile", "email", "user_friends"]
//Some other options: "user_about_me", "user_birthday", "user_hometown", "user_likes", "user_interests", "user_photos", "friends_photos", "friends_hometown", "friends_location", "friends_education_history"

func loginToFacebookWithSuccess(callingViewController: UIViewController, successBlock: () -> (), andFailure failureBlock: (NSError?) -> ()) {

    if FBSDKAccessToken.currentAccessToken() != nil {
        //For debugging, when we want to ensure that facebook login always happens
        //FBSDKLoginManager().logOut()
        //Otherwise do:
        return
    }

    FBSDKLoginManager().logInWithReadPermissions(facebookReadPermissions, fromViewController: callingViewController, handler: { (result:FBSDKLoginManagerLoginResult!, error:NSError!) -> Void in
        if error != nil {
            //According to Facebook:
            //Errors will rarely occur in the typical login flow because the login dialog
            //presented by Facebook via single sign on will guide the users to resolve any errors.

            // Process error
            FBSDKLoginManager().logOut()
            failureBlock(error)
        } else if result.isCancelled {
            // Handle cancellations
            FBSDKLoginManager().logOut()
            failureBlock(nil)
        } else {
            // If you ask for multiple permissions at once, you
            // should check if specific permissions missing
            var allPermsGranted = true

            //result.grantedPermissions returns an array of _NSCFString pointers
            let grantedPermissions = Array(result.grantedPermissions).map( {"\($0)"} )
            for permission in facebookReadPermissions {
                if !grantedPermissions.contains(permission) {
                    allPermsGranted = false
                    break
                }
            }
            if allPermsGranted {
                // Do work
                let fbToken = result.token.tokenString
                let fbUserID = result.token.userID

                //Send fbToken and fbUserID to your web API for processing, or just hang on to that locally if needed
                //self.post("myserver/myendpoint", parameters: ["token": fbToken, "userID": fbUserId]) {(error: NSError?) ->() in
                //  if error != nil {
                //      failureBlock(error)
                //  } else {
                //      successBlock(maybeSomeInfoHere?)
                //  }
                //}

                successBlock()
            } else {
                //The user did not grant all permissions requested
                //Discover which permissions are granted
                //and if you can live without the declined ones
                failureBlock(nil)
            }
        }
    })
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment