Search code examples
iosswiftfacebook-graph-apiparse-platformpfuser

Parse Facebook Login/Signup Not Working (Swift)


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:

  1. 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.

  2. 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)"

  1. 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!


Solution

  • 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 :)