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