I had tried a lot to achieve link existing parse user to Facebook.
I'm using swift 3.
But I can't find solution
My Status is
I have already account not linked Facebook.
I want link my account via facebookUtils
My app has "Sign In or SignUp Facebook" Button
It working like below
My problem is
Here is what I'm trying to do
get email address via facebookGraph API and check isUser or not via query for email
I had tried login like this
try! PFUser.logIn(withUsername: userObj.username!, password: userObj.password!)
but It is not working because I can't get password. It always returned nil
I had tried link PFUser Object which I had get via 1 query results
I also had tried cloud function like this
Parse.Cloud.define("fblink", function(request, response) {
var query = new Parse.Query(Parse.User);
query.equalTo("username", request.params.username);
query.first({
success: function(object) {
if (!Parse.FacebookUtils.isLinked(object)) {
Parse.FacebookUtils.link(object, null, {
success: function(user) {
alert("Woohoo, user logged in with Facebook!");
response.success(true);
},
error: function(user, error) {
alert("User cancelled the Facebook login or did not fully authorize.");
response.success(false);
}
});
}
},
error: function(error) {
console.log("Error: " + error.code + " " + error.message);
}
});
});
But It is not working...I think cloud function approach is not a solution in this case.
I had found a lot of questions related merging exist user to Facebook authdata. But I can't find any solution.
I hope find good way together.
func fbBtnTapped() {
//Show Activity indicator
let spiningActivity = MBProgressHUD.showAdded(to: self.view, animated: true)
spiningActivity?.labelText = "Loading"
spiningActivity?.detailsLabelText = "Please Wait"
let permissions = ["public_profile", "email"]
PFFacebookUtils.logInInBackground(withReadPermissions: permissions, block: {(user:PFUser?, error:Error?) -> Void in
if let user = user {
if user.isNew {
print("User signed up and logged in through Facebook!")
} else {
print("User logged in through Facebook!")
}
} else {
print("Uh oh. The user cancelled the Facebook login.")
}
if(error != nil)
{
spiningActivity?.hide(true)
print("in FBBUTTONTapped Error")
// display an error message
let userMessage = error!.localizedDescription
let myAlert = UIAlertController(title: "Alert", message: userMessage, preferredStyle: UIAlertControllerStyle.alert)
let okAction = UIAlertAction(title: "Ok", style: UIAlertActionStyle.default, handler: nil)
myAlert.addAction(okAction)
self.present(myAlert, animated: true, completion: nil)
return
}
// Load facebook details like userName, email address, profile picture.
self.loadFacebookUserDetails()
})
}
Here is what I had tried in loadFacebookUserDetails.
func loadFacebookUserDetails()
{
MBProgressHUD.hideAllHUDs(for: self.view, animated: true)
//Show Activity indicator
let spiningActivity = MBProgressHUD.showAdded(to: self.view, animated: true)
spiningActivity?.labelText = "Loading"
spiningActivity?.detailsLabelText = "Wait"
//Define fields we would like to read from Facebook User Object
let requestParameters = ["fields": "id, email, first_name, last_name"]
// me
let userDetails : FBSDKGraphRequest = FBSDKGraphRequest(graphPath: "me", parameters: requestParameters)
userDetails.start(completionHandler: { (connection, result, error) -> Void in
if error != nil {
print("in FBDetailed Error")
//Display error message
spiningActivity?.hide(true)
let userMessage = error!.localizedDescription
let myAlert = UIAlertController(title:"Alert", message: userMessage, preferredStyle: UIAlertControllerStyle.alert)
let okAction = UIAlertAction(title: "Ok", style: UIAlertActionStyle.default, handler: nil)
myAlert.addAction(okAction)
self.present(myAlert, animated: true, completion: nil)
PFUser.logOut()
return
}
var userId = String()
var userName = String()
var userEmail = String()
var userFirstName = String()
var userLastName = String()
var displayName = String()
var userObj = PFUser()
//Extract user fields
if let dict = result as? [String: AnyObject]
{
userId = dict["id"] as! String
print("User id=\(userId)")
userEmail = (dict["email"] as? String)!
print("User email=\(userEmail)")
userFirstName = dict["first_name"] as! String
print("User FirstName=\(userFirstName)")
userLastName = dict["last_name"] as! String
print("User LastName=\(userLastName)")
}
//get Username
if !userEmail.isEmpty
{
PFUser.current()?.email = userEmail
if let range = userEmail.range(of: "@") {
let username = userEmail.substring(to: range.lowerBound)
userName = username
PFUser.current()?.username = username
}
}
PFFacebookUtils.linkUser(inBackground: PFUser.current()!, with: FBSDKAccessToken.current())
//Get Facebook profile picture
let userProfile = "https://graph.facebook.com/" + userId + "/picture?type=large"
let profilePictureUrl = URL(string:userProfile)
let profilePictureData = try? Data(contentsOf: profilePictureUrl!)
//Prepare PFUser object
if(profilePictureData != nil)
{
let profileFileObject = PFFile(data:profilePictureData!)
// userObj.setObject(profileFileObject!, forKey: "profileImg")
if (PFUser.current()?.object(forKey: "profileImg")) == nil{
PFUser.current()?.setObject(profileFileObject!, forKey: "profileImg")
}
}
displayName=userFirstName+userLastName
PFUser.current()?.setObject(displayName, forKey: "displayname")
//Check If user has already signedup or not
let query = PFQuery(className: "_User")
query.whereKey("username", equalTo: userName)
try! userObj = query.getFirstObject() as! PFUser
// if userObj.username != nil {
//
// print("already signed up")
// return
// }
// print(FBSDKAccessToken.current())
// try! PFUser.logIn(withUsername: userObj.username!, password: userObj.password!)
PFFacebookUtils.linkUser(inBackground: PFUser.current()!, with: FBSDKAccessToken.current())
// userObj.setObject(displayName, forKey: "displayname")
PFUser.current()?.saveInBackground(block: { (success:Bool, error:Error?) in
spiningActivity?.hide(true)
if(error != nil)
{
let userMessage = error!.localizedDescription
let myAlert = UIAlertController(title: "Alert2", message: userMessage, preferredStyle: UIAlertControllerStyle.alert)
let okAction = UIAlertAction(title: "Ok", style: UIAlertActionStyle.default, handler: nil)
myAlert.addAction(okAction)
self.present(myAlert, animated: true, completion: nil)
PFUser.logOut()
return
}
if(success)
{
if !userId.isEmpty
{
print(userId)
//userId->userName
UserDefaults.standard.set(userName, forKey: "username")
UserDefaults.standard.synchronize()
DispatchQueue.main.async{
let appDelegate:AppDelegate = UIApplication.shared.delegate as! AppDelegate
appDelegate.buildUserInterface()
}
}
}
})
//
})
}
To be able to link the account you will need PFUser.current()
if he is not a Facebook linked account. Your options are:
1:) Logs in with facebook via the login page with your logic. This means that you have a new user in the Users
column. So essentially that can be used as a parse
login regardless later. You can just update the username with maybe email or facebook name ( because facebook login will fill the username with the ID
by default ). Once he gets to the success page, you can ask him to fill in a password for later if he chooses to not use his facebook account in future. Then he is welcome to unlink the account in settings page with a unlink button.
@IBAction func unlinkFacebook(_ sender: AnyObject) {
let user = PFUser.current()
showHUD(message: "unlinking")
PFFacebookUtils.unlinkUser(inBackground: user!, block:{
(succeeded: Bool?, error) -> Void in
if succeeded! {
self.simpleAlert(mess: "Your account is no longer associated with your Facebook account.")
self.hideHUD()
}else{
self.simpleAlert(mess: "\(error!.localizedDescription)")
self.hideHUD()
}
})
}
all the unlinking will do is clear the authData
column.
2:) Logs in as a parse user, once successfully logged in ask him to link his account to facebook. a func
maybe like:
func linkFacebook() {
let user = PFUser.current()
showHUD(message: "linking")
if !PFFacebookUtils.isLinked(with: user!) {
PFFacebookUtils.linkUser(inBackground: user!, withReadPermissions: nil, block:{
(succeeded: Bool?, error) -> Void in
if succeeded! {
self.simpleAlert(mess: "Success. You can now log in using your facebook account in future.")
self.hideHUD()
}else{
self.simpleAlert(mess: "\(error!.localizedDescription)")
self.hideHUD()
}
})
}
}
Could put that in your viewdidLoad
or a nice button that gives the user a choice if he chooses to want to link his account. bare in mind you will need to call the Facebook sdk
to update the user details if you want to store the Facebook details.
Only column that is populated in the Users table
is authData
.