I've been frustratingly trying to implement Parse's Facebook login function into my app using Swift for over a month and can't seem to figure it out. I successfully linked my app to both Facebook and Parse separately, but I can't make a Parse user using Facebook's login. I believe the AppDelegate is setup correctly, so I will only show my LoginViewController and hopefully someone who knows what they're doing can please help me out:
import UIKit
import Parse
import FBSDKCoreKit
import FBSDKLoginKit
protocol LoginViewControllerDelegate {
func onRegister(loginViewController : LoginViewController)
func onFacebookLogin(loginViewController : LoginViewController)
func onLogin(loginViewController : LoginViewController)
}
class LoginViewController: UIViewController, FBSDKLoginButtonDelegate {
//MARK: - Outlets
#IBOutlet weak var lblStatus: UILabel!
//var delegate : LoginViewControllerDelegate?
// Set permissions required from the facebook user account
let permissions = [ "user_about_me", "user_relationships", "user_location", "user_birthday", "public_profile", "user_friends", "user_email", "user_gender"]
//MARK: - Initial Load
override func viewDidLoad() {
super.viewDidLoad()
self.lblStatus.alpha = 0
//Facebook
if (FBSDKAccessToken.currentAccessToken() != nil)
{
// User is already logged in, do work such as go to next view controller.
}
else
{
let loginView : FBSDKLoginButton = FBSDKLoginButton()
// self.view.addSubview(loginView)
// loginView.center = self.view.center
// loginView.readPermissions = ["public_profile", "email", "user_friends"]
loginView.delegate = self
}
// ---------------------------- Check if user is logged in ---------------------
if PFUser.currentUser() != nil {
println("parse: User already logged in")
performSegueWithIdentifier("loggedIn", sender: self)
}
}
//MARK: - Facebook Login Button
func loginButton(loginButton: FBSDKLoginButton!, didCompleteWithResult result: FBSDKLoginManagerLoginResult!, error: NSError!) {
if ((error) != nil) {
println(error) // Process error
} else if result.isCancelled { // Handle cancellations
} else {
// If you ask for multiple permissions at once, you
// should check if specific permissions missing
if result.grantedPermissions.contains("email")
{
// Do work
}
}
returnUserData()
}
#IBAction func facebookLoginDidPress(sender: AnyObject) {
self.lblStatus.alpha = 0
PFFacebookUtils.logInInBackgroundWithReadPermissions(self.permissions, block: {
(user: PFUser?, error: NSError?) -> Void in
if (user == nil) {
if (error == nil) {
println("User cancelled FB login")
self.lblStatus.alpha = 1
}else{
println("FB login error: \(error)")
self.lblStatus.alpha = 1
}
} else if user!.isNew {
println("User signed up and logged in with Facebook! \(user)")
self.requestFacebook()
self.returnUserData()
self.performSegueWithIdentifier("loggedIn", sender: self)
} else {
println("User logged in via Facebook \(user)")
self.performSegueWithIdentifier("loggedIn", sender: self)
}
})
}
func requestFacebook() {
let graphRequest : FBSDKGraphRequest = FBSDKGraphRequest(graphPath: "me", parameters: nil)
graphRequest.startWithCompletionHandler({ (connection, result, error) -> Void in
if ((error) != nil)
{
// Process error
println("Error: \(error)")
}
else if error == nil
{
var userData: NSDictionary = NSDictionary(objectsAndKeys: result)
var facebookID: AnyObject? = userData["id"]
var name: AnyObject? = userData["first_name"]
var gender: AnyObject? = userData["gender"]
var birthday: AnyObject? = userData["birthday"]
var pictureURL = "https://graph.facebook.com/\(facebookID)/picture?type=large&return_ssl_resources=1"
var URLRequest = NSURL(string: pictureURL)
var URLRequestNeeded = NSURLRequest(URL: URLRequest!)
NSURLConnection.sendAsynchronousRequest(URLRequestNeeded, queue: NSOperationQueue.mainQueue(), completionHandler: {(response: NSURLResponse!,data: NSData!, error: NSError!) -> Void in
if error == nil {
var picture = PFFile(data: data)
PFUser.currentUser()!.setObject(picture, forKey: "profilePicture")
PFUser.currentUser()!.saveInBackground()
}
else {
println("Error: \(error.localizedDescription)")
}
})
}
})
}
func loginButtonDidLogOut(loginButton: FBSDKLoginButton!) {
println("User Logged Out")
}
func returnUserData()
{
let graphRequest : FBSDKGraphRequest = FBSDKGraphRequest(graphPath: "me", parameters: nil)
graphRequest.startWithCompletionHandler({ (connection, result, error) -> Void in
if ((error) != nil)
{
// Process error
println("Error: \(error)")
}
else
{
if let userName : NSString = result.valueForKey("name") as? NSString {
println("User Name is: \(userName)")
} else {println("No username fetched")}
if let userEmail : NSString = result.valueForKey("email") as? NSString {
println("User Email is: \(userEmail)")
} else {println("No email address fetched")}
if let userGender : NSString = result.valueForKey("gender") as? NSString {
println("User Gender is: \(userGender)")
} else {println("No gender fetched") }
}
})
}
}
Some important notes to make:
I am using the newest version of Parse (1.7.4) and FBSDK (4.1), so most other methods I've found online do not work anymore because certain functions or members have been removed since then.
The "func loginButton" is what I used for the FBSDKLoginButton, while the "#IBAction func facebookLoginDidPres" is for a regular UIButton I tried using for logging in. Based on what I've learned recently, I believe the UIButton method is the one I should go with, but that one leads to a Thread 1: Signal SIGABRT error stating:
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '+[PFFacebookUtils logInInBackgroundWithReadPermissions:block:]: unrecognized selector sent to class 0x10ed3ed80'
*** First throw call stack:
...
libc++abi.dylib: terminating with uncaught exception of type NSException
(lldb) ""
So I placed an exception breakpoint and it ended at the end of the "logInInBackgroundWithReadPermissions" function in my IBAction with the console stating:
"[PFFacebookUtils logInInBackgroundWithReadPermissions:block:]: unrecognized selector sent to class 0x10dbe2db0 (lldb)"
The last detail I feel is worth mentioning is that my console states "parse: User already logged in" in when I run the app, however there is no User created when I check Parse. This is getting printed from the section under "check if the user is logged in" section of my LogInViewController's viewDidLoad function.
I will be forever grateful to anyone who can help me figure out this problem that's been puzzling me for far too long. Thank you in advance!
I deleted the old PFFacebookUtils Framework, but kept the PFFacebookUtilsV4 Framework, and that solved the problem for me! Hopefully that helps anyone else with this problem :)
I have the same problem, my solution is to define Login behaviour = Web
PFFacebookUtils.facebookLoginManager().loginBehavior = FBSDKLoginBehavior.Web
When I call this before call loginInBackground..., after clicking to the "OK" button, my application received call-back with a user (not nil) and PFUser.currentUser is updated with that user.
Related
my name is Vincent and I need your help !
(My code is not exhaustive)
No problem to login or logout with the Facebook SDK ... BUT
When I start my app, no access token and when i click on the Login button, it prints me an accessToken, then I logout.
How to get the accessToken when the app starts ?
import UIKit
import FBSDKCoreKit
import FBSDKLoginKit
class HomeViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
getUserStatuts()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
// MARK -> Facebook Connect
func loginToFacebook() {
if let fbToken = FBSDKAccessToken.currentAccessToken() {
print("Acces token : \(fbToken)")
loginManager.logOut()
print("Logout")
} else {
print("Login")
loginManager.logInWithPublishPermissions(publishPermissions, fromViewController: nil, handler: {
(result:FBSDKLoginManagerLoginResult!, error:NSError!) -> Void in
if error == nil {
self.getUserData()
} else {
print(error.localizedDescription)
}
})
}
}
func getUserData() {
let readPermissions = ["public_profile", "email", "user_friends", "user_birthday"]
let parameters = ["fields": "id, first_name, last_name, name, birthday, picture.type(large)"]
let request = FBSDKGraphRequest(graphPath: "me", parameters: parameters)
request.startWithCompletionHandler({
(connection, result, error) -> Void in
if error == nil {
// display info
}
})
}
In the login function you want to implement the
let fbLogin = FBSDKLoginManager()
fblogin.logout()
the fblogin.logout() must be implemented before fbLogin.logInWithReadPermissions(["email"], fromViewController: self) { (facebookResult, facebookError) in
if facebookError != nil { ` to clear any access tokens from previous login.
I have an iPad app and its used in a hotel for guests. So multiple users use the app through Facebook login and after the usage I need to logout the user from the app. So the next user will have the login screen again. I'm doing this in logout process and when I call the login function Its not giving the login screen. Instead it give me the already authorized screen. (With ok and cancel button). Please help to resolve this issue.
To login:
FBSDKLoginManager().logInWithReadPermissions(["email", "public_profile"], fromViewController: self, handler: { (result, error) -> Void in
if error != nil {
print("error : \(error.localizedDescription)")
} else if result.isCancelled {
print("user cancelled")
} else {
print("success")
}
})
To Logout :
FBSDKLoginManager().logOut()
FBSDKAccessToken.setCurrentAccessToken(nil)
FBSDKProfile.setCurrentProfile(nil)
let storage = NSHTTPCookieStorage.sharedHTTPCookieStorage()
for cookie in storage.cookies! {
storage.deleteCookie(cookie)
}
NSUserDefaults.standardUserDefaults().synchronize()
It will always give you an already authorized screen because Facebook is not responsible to logout from the Safari browser.
You are logged out from the login manager it is fine.
For this situation you can use the loginBehavior property of FBSDKLoginManager
You have to set the behavior to the Web it will open popup to login.
let fbManager : FBSDKLoginManager = FBSDKLoginManager()
fbManager.loginBehavior = FBSDKLoginBehavior.Web
fbManager.logInWithReadPermissions(["email"], fromViewController: self) { (result, error) -> Void in
if error != nil {
print(error)
}
else {
print(result)
}
}
You can do the logout from the manager as per your need.
Hope it will help you.
clear your facebook token from ios like this... this make user to login every time in app
ACAccountStore *store = [[ACAccountStore alloc] init];
NSArray *fbAccounts = [store accountsWithAccountType:[store accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierFacebook]];
for (ACAccount *fb in fbAccounts) {
[store renewCredentialsForAccount:fb completion:^(ACAccountCredentialRenewResult renewResult, NSError *error) {
}];
}
This works for me
For objective-C
1st Define
#property(nonatomic,strong)FBSDKLoginManager *login;
Then Use this method.
-(void)logoutFromFacebook{
if(self.login){
[self.login logOut];
}else{
self.login = [[FBSDKLoginManager alloc] init];
[self.login logOut];
}
}
For Swift
var login: FBSDKLoginManager?
func FBSDKLoginManager () {
if self.login != nil {
self.login.logout()
}else {
self.login = self.login()
self.login.logout()
}
}
Hope this will help.
Here is a working solution using their custom button class FBSDKLoginButton on a view. You should only enter the protected page on view will appear if the currentAccessToken is not nil. This will prevent showing the first view for a few miliseconds. As you can see, I embeded my viewControllers in a NaviguationController.
UIView with custom class in the login view :
ViewWillAppear :
override func viewWillAppear(animated: Bool) {
super.viewDidAppear(animated)
if (FBSDKAccessToken.currentAccessToken() != nil) {
loginFacebook(FBSDKAccessToken.currentAccessToken().tokenString,
userId: FBSDKAccessToken.currentAccessToken().userID)
self.enterProtectedPage();
}
}
ViewDidLoad :
override func viewDidLoad() {
super.viewDidLoad()
loginBtn.delegate = self
loginBtn.readPermissions = ["public_profile", "email", "user_birthday", "user_relationship_details"];
}
login button click action :
func loginButton(loginButton: FBSDKLoginButton!, didCompleteWithResult result: FBSDKLoginManagerLoginResult!, error: NSError!) {
if (error != nil) {
print(error.localizedDescription)
return
}
if let _ = result.token {
loginFacebook(FBSDKAccessToken.currentAccessToken().tokenString,
userId: FBSDKAccessToken.currentAccessToken().userID)
self.enterProtectedPage();
}
}
enter protected page :
func enterProtectedPage() {
let protectedPage = self.storyboard?.instantiateViewControllerWithIdentifier("ProtectedPageViewController") as!
ProtectedPageViewController
let protectedPageNav = UINavigationController(rootViewController: protectedPage)
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
appDelegate.window?.rootViewController = protectedPageNav
}
logout action on the protected page :
#IBAction func btnLogoutClicked(sender: AnyObject) {
let loginManager = FBSDKLoginManager()
loginManager.logOut()
let loginPage = self.storyboard?.instantiateViewControllerWithIdentifier("ViewController") as!ViewController
let loginPageNav = UINavigationController(rootViewController: loginPage)
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
appDelegate.window?.rootViewController = loginPageNav
}
Where is the latest Facebook Swift Documentation. I can't get the FB Login Dialog to show up. The call to loginWithReadPermissions never returns?
import UIKit
import FBSDKCoreKit
import FBSDKLoginKit
class ViewController: UIViewController {override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let loginManager = FBSDKLoginManager()
loginManager.logInWithReadPermissions(["basic_info", "email", "user_likes"], fromViewController: self.parentViewController, handler: { (result, error) -> Void in
if error != nil {
print(FBSDKAccessToken.currentAccessToken())
} else if result.isCancelled {
print("Cancelled")
} else {
print("LoggedIn")
}
})
}
You code should work if you exclude "user_likes" from permissions.
From facebook docs:
If your app asks for more than public_profile, email and user_friends, Facebook must review it before you release it. Learn more about the review process and what's required to pass review.
https://developers.facebook.com/docs/facebook-login/permissions/v2.5#reference-user_likes
Another problem may be that you have not set correctly the FacebookSDK in your project. See this tutorial: https://developers.facebook.com/docs/ios/getting-started/
So the answer is to use FBSDKLoginButton with the following
In the class declaration of the viewController
import FBSDKCoreKit
import FBSDKLoginKit
class ViewController: UIViewController, FBSDKLoginButtonDelegate
Then show the FBLogin Button with
// Setup the FB Login/Logout Button FB will take care of the
// verbiage based on the current access token
let loginView : FBSDKLoginButton = FBSDKLoginButton()
self.view.addSubview(loginView)
loginView.center = self.view.center
loginView.readPermissions = ["public_profile", "email", "user_friends"]
loginView.delegate = self
// If we have an access token, then let's display some info
if (FBSDKAccessToken.currentAccessToken() != nil)
{
// Display current FB premissions
print (FBSDKAccessToken.currentAccessToken().permissions)
// Since we already logged in we can display the user datea and taggable friend data.
self.showUserData()
self.showFriendData()
}
Show the users info with
func showUserData()
{
let graphRequest : FBSDKGraphRequest = FBSDKGraphRequest(graphPath: "me", parameters: ["fields" : "id, name, gender, first_name, last_name, locale, email"])
graphRequest.startWithCompletionHandler({ (connection, result, error) -> Void in
if ((error) != nil)
{
// Process error
print("Error: \(error)")
}
else
{
let userName : NSString = result.valueForKey("name") as! NSString
print("User Name is: \(userName)")
if let userEmail : NSString = result.valueForKey("email") as? NSString {
print("User Email is: \(userEmail)")
}
}
})
}
And to display the taggable friends
func showFriendData()
{
let graphRequest : FBSDKGraphRequest = FBSDKGraphRequest(graphPath: "me/taggable_friends?limit=999", parameters: ["fields" : "name"])
graphRequest.startWithCompletionHandler({ (connection, result, error) -> Void in
if ((error) != nil)
{
// Process error
print("Error: \(error)")
}
else
{
if let friends : NSArray = result.valueForKey("data") as? NSArray{
var i = 1
for obj in friends {
if let name = obj["name"] as? String {
print("\(i) " + name)
i++
}
}
}
}
})
}
I am trying to add facebook login for my app. I am using the FBSDKLoginButton which comes with its own button and once you login it turns to logout. I don't want all that. I just need to add my own button a view. And in that button's #IBAction I want the facebook login to take place. The code which I am now using is
class ViewController: UIViewController,FBSDKLoginButtonDelegate {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
if (FBSDKAccessToken.currentAccessToken() != nil)
{
// User is already logged in, do work such as go to next view controller.
}
else
{
let loginView : FBSDKLoginButton = FBSDKLoginButton()
self.view.addSubview(loginView)
loginView.center = self.view.center
loginView.readPermissions = ["public_profile", "email", "user_friends"]
loginView.delegate = self
}
}
func loginButton(loginButton: FBSDKLoginButton!, didCompleteWithResult result: FBSDKLoginManagerLoginResult!, error: NSError!) {
println("User Logged In")
if ((error) != nil)
{
// Process error
}
else if result.isCancelled {
// Handle cancellations
}
else {
returnUserData()
// If you ask for multiple permissions at once, you
// should check if specific permissions missing
if result.grantedPermissions.contains("email")
{
// Do work
}
}
}
func loginButtonDidLogOut(loginButton: FBSDKLoginButton!) {
println("User Logged Out")
}
func returnUserData()
{
let graphRequest : FBSDKGraphRequest = FBSDKGraphRequest(graphPath: "me", parameters: nil)
graphRequest.startWithCompletionHandler({ (connection, result, error) -> Void in
if ((error) != nil)
{
// Process error
println("Error: \(error)")
}
else
{
println("fetched user: \(result)")
let userName : NSString = result.valueForKey("name") as! NSString
println("User Name is: \(userName)")
let userEmail : NSString = result.valueForKey("email") as! NSString
println("User Email is: \(userEmail)")
}
})
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
How do I customise this and achieve what I need. Thanks in advance.
You might want to check the latest FacebookSDK, here's what i have:
#IBAction func connectWithFacebook(){
if FBSDKAccessToken.currentAccessToken() != nil {
FBSDKLoginManager().logOut()
return
}
let login:FBSDKLoginManager = FBSDKLoginManager()
login.logInWithReadPermissions(["email"], handler: { (result:FBSDKLoginManagerLoginResult!, error:NSError!) -> Void in
if(error != nil){
FBSDKLoginManager().logOut()
}else if(result.isCancelled){
FBSDKLoginManager().logOut()
}else{
//Handle login success
}
})
}
Otherwise with the older SDK you can do the following:
Add the FBSDKLoginButton to your view and set it invisible
Add your custom button
On your custom button IBAction perform a click on the FBSDKLoginButton with the method sendActionsForControlEvents
FBSDKLoginManager can be used if FB Login is handled via custom button.
For more info, your can check this.
Set current access is nil.
This calls [FBSDKAccessToken setCurrentAccessToken:nil] and
[FBSDKProfile setCurrentProfile:nil].
FBSDKAccessToken.setCurrentAccessToken(nil)
For Facebook logout.
I'm trying to make a facebook login for my app to connect with facebook, everything went good, I created a login page added an imageView to show the user facebook's profile photo, a label to display the name of the user, I managed to display the image of the user when I loged in to Facebook, but I couldn't connect the name's label to the code to display the user name! can anybody help please ? This is my code :
import UIKit
import FBSDKCoreKit
import FBSDKShareKit
import FBSDKLoginKit
class ViewController: UIViewController,FBSDKLoginButtonDelegate {
#IBOutlet weak var nameLabel: UILabel!
#IBOutlet weak var profileImageView: FBSDKProfilePictureView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
if (FBSDKAccessToken.currentAccessToken() != nil)
{
// User is already logged in, do work such as go to next view controller.
}
else
{
let loginView : FBSDKLoginButton = FBSDKLoginButton()
self.view.addSubview(loginView)
loginView.center = self.view.center
loginView.readPermissions = ["public_profile", "email", "user_friends"]
loginView.delegate = self
}
}
// Facebook Delegate Methods
func loginButton(loginButton: FBSDKLoginButton!, didCompleteWithResult result: FBSDKLoginManagerLoginResult!, error: NSError!) {
println("User Logged In")
if ((error) != nil)
{
// Process error
}
else if result.isCancelled {
// Handle cancellations
}
else {
// If you ask for multiple permissions at once, you
// should check if specific permissions missing
if result.grantedPermissions.contains("email")
{
// Do work
}
}
}
func loginButtonDidLogOut(loginButton: FBSDKLoginButton!) {
println("User Logged Out")
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func returnUserData()
{
let graphRequest : FBSDKGraphRequest = FBSDKGraphRequest(graphPath: "me", parameters: nil)
graphRequest.startWithCompletionHandler({ (connection, result, error) -> Void in
if ((error) != nil)
{
// Process error
println("Error: \(error)")
}
else
{
println("fetched user: \(result)")
let userName : NSString = result.valueForKey("name") as! NSString
println("User Name is: \(userName)")
let userEmail : NSString = result.valueForKey("email") as! NSString
println("User Email is: \(userEmail)")
}
})
}
}
If you go through the code in the debugger, is userName getting set to the user's name? I don't see anywhere in your code where you're actually setting the UILabel nameLabel's text to your userName var.
Could you provide some more detail? If this was simply an oversight, the problem is you need to set nameLabel's text to userName.
nameLabel.text = userName
If this wasn't an oversight and userName is not getting set to the proper value of the user's name, you're most likely doing something incorrectly in the Facebook SDK.
not sure if you figured this out yet, but I was having the same problem I had the profile pic displaying but not the username. Anywho I just added the last line 'self.' to your code and it worked:
let userName : NSString = result.valueForKey("name") as! NSString
print("Username is: \(userName)")
self.nameResult.text = userName as String
swap 'nameResult' for your labels name 'nameLabel' and that should work.