Retrieve email FacebookSDK and Swift - facebook

I'm successfully implemented facebook login in my app, but I'm not able to query/retrieve the email address from the user.
I have the following code:
import UIKit
class testViewController: 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.
// Or Show Logout Button
let loginView : FBSDKLoginButton = FBSDKLoginButton()
self.view.addSubview(loginView)
loginView.center = self.view.center
loginView.readPermissions = ["public_profile", "email", "user_friends"]
loginView.delegate = self
self.returnUserData()
}
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
}
self.returnUserData()
}
}
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.
}
}
And the println output:
fetched user: {
id = 808101565906152;
name = "Marcelo Pontes Machado";
}
When I try to display the email I got a nill value.
I think the code is right, maybe some SDK that I miss to import?

Put the fields you want back from the Graph request in your parameters
FBSDKGraphRequest(graphPath: "me", parameters: ["fields":"id,email,name,picture.width(480).height(480)"]).startWithCompletionHandler({
Because the Graph API may sometimes only return a minimum amount of information unless otherwise requested.

In Swift
if((FBSDKAccessToken.current()) != nil)
{
FBSDKGraphRequest(graphPath: "me", parameters: ["fields": "id, name, first_name, last_name, picture.type(large), email"]).start(completionHandler:
{ (connection, result, error) -> Void in
if (error == nil)
{
//everything works print the user data
// print(result!)
if let data = result as? NSDictionary
{
let firstName = data.object(forKey: "first_name") as? String
let lastName = data.object(forKey: "last_name") as? String
if let email = data.object(forKey: "email") as? String
{
print(email)
}
else
{
// If user have signup with mobile number you are not able to get their email address
print("We are unable to access Facebook account details, please use other sign in methods.")
}
}
}
})
}
In Objective C wire this code
// START_LOADING;
[[[FBSDKGraphRequest alloc] initWithGraphPath:#"me"
parameters:#{#"fields": #"picture, email,first_name,last_name"}]
startWithCompletionHandler:^(FBSDKGraphRequestConnection *connection, id result, NSError *error)
{
if (!error)
{
if ([result valueForKey:#"email"] != nil)
{
NSLog([result valueForKey:#"email"]);
}
else
{
// If user have signup with mobile number you are not able to get their email address
NSLog(#"We are unable to access Facebook account details, please use other sign in methods.");
// STOP_LOADING;
}
}
}];

Related

How do I update UILabels synchronously with Firestore data?

I'm currently building an iOS app that will synchronize account information from Firestore. I have the login/register process hooked up and working. However, I need help understanding how to update my logInOutBtn, fullNameTxt and emailTxt in my MenuVC automatically when an user logs in/out. Currently, it will update whenever I close then reopen the menu, but what should I use to automatically update it without having to close the menu? Thanks!
// MenuVC
override func viewDidAppear(_ animated: Bool) {
if let user = Auth.auth().currentUser , !user.isAnonymous {
// We are logged in
logInOutBtn.setTitle("Logout", for: .normal)
if UserService.userListener == nil {
UserService.getCurrentUser {
self.fullNameTxt.text = UserService.user.fullName
self.emailTxt.text = UserService.user.email
}
}
} else {
logInOutBtn.setTitle("Login", for: .normal)
self.fullNameTxt.text = "Sign in or create an account"
self.emailTxt.text = "to continue."
}
}
fileprivate func presentLoginController() {
let storyboard = UIStoryboard(name: Storyboard.LoginStoryboard, bundle: nil)
if #available(iOS 13.0, *) {
let controller = storyboard.instantiateViewController(identifier: StoryboardId.LoginVC)
present(controller, animated: true, completion: nil)
} else {
// Fallback on earlier versions
}
}
#IBAction func logInOutClicked(_ sender: Any) {
guard let user = Auth.auth().currentUser else { return }
if user.isAnonymous {
presentLoginController()
} else {
do {
try Auth.auth().signOut()
UserService.logoutUser()
Auth.auth().signInAnonymously { (result, error) in
if let error = error {
debugPrint(error)
Auth.auth().handleFireAuthError(error: error, vc: self)
}
self.presentLoginController()
}
} catch {
debugPrint(error)
Auth.auth().handleFireAuthError(error: error, vc: self)
}
}
}
// UserService
func getCurrentUser(completion: #escaping () -> ()) {
guard let authUser = auth.currentUser else { return }
let userRef = db.collection("users").document(authUser.uid)
userListener = userRef.addSnapshotListener({ (snap, error) in
if let error = error {
debugPrint(error.localizedDescription)
return
}
guard let data = snap?.data() else { return }
self.user = User.init(data: data)
completion()
})
// User Model
struct User {
var fullName: String
var address: String
var id: String
var email: String
var stripeId: String
init(fullName: String = "",
address: String = "",
id: String = "",
email: String = "",
stripeId: String = "") {
self.fullName = fullName
self.address = address
self.id = id
self.email = email
self.stripeId = stripeId
}
init(data: [String : Any]) {
fullName = data["fullName"] as? String ?? ""
address = data["address"] as? String ?? ""
id = data["id"] as? String ?? ""
email = data["email"] as? String ?? ""
stripeId = data["stripeId"] as? String ?? ""
}
static func modelToData(user: User) -> [String : Any] {
let data : [String : Any] = [
"fullName" : user.fullName,
"address" : user.address,
"id" : user.id,
"email" : user.email,
"stripeId" : user.stripeId
]
return data
}
}
// My app menu
The signout process is pretty straightforward and is marked as throws so if it fails, it will generate an error that can be handled by a catch. It is not asynchronous so it won't have (or need) a closure.
So simply stated
func signOut() {
let firebaseAuth = Auth.auth()
do {
try firebaseAuth.signOut()
print("successful signout")
self.logInOutBtn.setTitle("Log In", for: .normal)
self.fullNameTxt.text = ""
self.emailTxt.text = ""
} catch let signOutError as NSError {
print ("Error signing out: %#", signOutError)
//present the error to the user/handle the error
}
}
The signIn function is asynchronous with a closure so when the user signs in successfully, the code in the closure will fire and that's the perfect place to update the UI.
Auth.auth().signIn(withEmail: email, password: password) { [weak self] authResult, error in
guard let strongSelf = self else { return }
// update the UI here.
}
You can also just monitor the authState with an observer and have it react to users logging in/out
self.authListener = Auth.auth()?.addAuthStateDidChangeListener { auth, user in
if let theUser = user {
print("User logged in \(theUser)") // User is signed in.
self.dismissViewControllerAnimated(true, completion: nil)
} else {
print("Need to login.") // No user is signed in.
//present login view controller
}
}
If you no longer want to observe the auth state, you can remove it with
Auth.auth()?.removeAuthStateDidChangeListener(self.authListener)

Swift Firebase Facebook login dialog box pop up 2 times

I have a facebook login which using firebase to authenticate the process.
However, after I input my login detail and press confirm. It will back to the login page and pop up the facebook login page again. Then I press confirm again. It will display "User Cancel Login".
I am not sure why does it happen 2 times also when i click the confirm button it will display "User Cancel Login"
func loginButton(FbLoginBtn: FBSDKLoginButton!, didCompleteWithResult result: FBSDKLoginManagerLoginResult!, error: NSError!) {
let FbloginManager = FBSDKLoginManager()
FbloginManager.logInWithReadPermissions(["email","public_profile", "user_location", "user_hometown","user_friends"],fromViewController: self, handler: { (result, error) in
if let error = error {
print(error.localizedDescription)
return
}else if(result.isCancelled) {
print("User Cancel Login")
}else{
let credential = FIRFacebookAuthProvider.credentialWithAccessToken(FBSDKAccessToken.currentAccessToken().tokenString)
print("User\(self.user?.displayName) login successful")
AppState.instance.signedIn = true
if AppState.instance.signedIn == false{
self.firebaseLogin(credential)
//self.createFirebaseUser()
self.performSegueWithIdentifier(SEGUE_LOGIN, sender: nil)
}
}
})
}
For me this code work :
#IBAction func btnFBLoginPressed(sender: AnyObject) {
self.comeFromFB = true
let fbLoginManager : FBSDKLoginManager = FBSDKLoginManager()
var id:String = ""
var urlPhoto:String = ""
fbLoginManager.logInWithReadPermissions(["email"], fromViewController: self){ (result, error) -> Void in
if let error = error
{
print(error)
return
}
else
{
let fbloginresult : FBSDKLoginManagerLoginResult = result
if (!result.isCancelled)
{
if(fbloginresult.grantedPermissions.contains("email"))
{
if((FBSDKAccessToken.currentAccessToken()) != nil){
FBSDKGraphRequest(graphPath: "me", parameters: ["fields": "id, name, first_name, last_name, picture.type(large), email"]).startWithCompletionHandler({ (connection, result, error) -> Void in
let bUserFacebookDict = result as! NSDictionary
id = bUserFacebookDict["id"]! as! String
urlPhoto = "https://graph.facebook.com/"+id+"/picture?width=500&height=500"
let credential = FIRFacebookAuthProvider.credentialWithAccessToken(FBSDKAccessToken.currentAccessToken().tokenString)
FIRAuth.auth()?.signInWithCredential(credential) { (user, error) in
self.currentUser.setCurrentUserState(user!.uid, _firstName: bUserFacebookDict["first_name"]! as! String, _name: bUserFacebookDict["last_name"]! as! String, _urlPhoto: urlPhoto, _email:bUserFacebookDict["email"]! as! String, _connected:true)
}
})
}
}
}
}
}
}
Then I add a listener in the ViewDidAppear method with perform segue after "connected" state.

Can not fetch email from Facebook in Swift

EDIT: The problem with this question is a bit silly. As in the answer the problem is that I have not given the parameters which I want.
I can not get through with it anymore :) What can be the problem?
func returnUserData()
{
let graphRequest : FBSDKGraphRequest = FBSDKGraphRequest(graphPath: "me", parameters: nil)
graphRequest.startWithCompletionHandler({ (connection, result, error) -> Void in
if ((error) != nil)
{
// Process error
print("Error: \(error)")
}
else
{
print("fetched user: \(result)")
let userName : NSString = result.valueForKey("name") as! NSString
print("User Name is: \(userName)")
let userEmail : NSString = result.valueForKey("email") as! NSString
print("User Email is: \(userEmail)")
}
})
}
I call the function like that:
func loginButton(loginButton: FBSDKLoginButton!,
didCompleteWithResult result: FBSDKLoginManagerLoginResult!,
error: NSError!) {
print("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
returnUserData()
}
}
}
I get the error:
My problem is about the fatal error. However, I am kindly waiting for the comments on 2nd and 3rd lines of the output. Because, I get this output every time but the app runs correctly.
You must specify the parameters for the request.
let graphRequest : FBSDKGraphRequest = FBSDKGraphRequest(graphPath: "me", parameters: ["fields": "first_name, last_name, email"])

Can't pinpoint FBSDKGraphRequest crash

I'm using FB Login, and my app is crashing every now and then within that method. It works fine for me, but not for some other users. In this function, I'm setting user data in Parse with data received from the FBSDKGraphRequest.
// Sends FB Graph Request and sets user attributes in Parse
func setUserData() {
var user = PFUser.currentUser()!
let graphRequest : FBSDKGraphRequest = FBSDKGraphRequest(graphPath: "me", parameters: nil)
graphRequest.startWithCompletionHandler({ (connection, result, error) -> Void in
if ((error) != nil)
{
println("Set user values error: \(error)")
}
else
{
firstName = result.valueForKey("first_name") as! NSString
lastName = result.valueForKey("last_name") as! NSString
user["name"] = "\(firstName) \(lastName)"
NSUserDefaults.standardUserDefaults().setObject("\(firstName) \(lastName)", forKey: "name")
id = result.valueForKey("id") as! NSString
user["fbID"] = id
gender = result.valueForKey("gender") as! NSString
user["gender"] = gender
email = result.valueForKey("email") as! NSString
user["email"] = email
user["score"] = 100
user.saveInBackgroundWithBlock({ (success, error) -> Void in
if success {
objID = user.objectId!
}
})
self.performSegueWithIdentifier("segue", sender: self)
}
})
}
Now, in Crashlytics, I'm getting EXC_BREAKPOINT, but can't figure out exactly where the crash is coming from or what to do about it. Looks like it may be coming from Facebook's side? Any help would be appreciated.
I've had the same issue. I forgot to put the permissions on the FBSDKLoginButton:
facebookLoginButton.readPermissions = ["public_profile", "email", "user_friends"];
Maybe you forgot it too?

Add Facebook "name" to PFUser ["fullname"] in swift

I have a registration/login method (from PFFacebookUtilsV4) that successfully adds a new PFUser object to my Parse database when a new Facebook user logs in. Within that method I make an FBSDKGraphRequest to get the basic user data (name, email). However I am having trouble adding the name and email to the newly created PFUser object. Here's my code so far:
#IBAction func facebookLogin(sender: AnyObject) {
PFFacebookUtils.logInInBackgroundWithReadPermissions(["public_profile","email","user_friends"], block: { (user: PFUser?, error: NSError?) -> Void in
if let user = user {
if user.isNew {
let graphRequest : FBSDKGraphRequest = FBSDKGraphRequest(graphPath: "me", parameters: ["fields":"name,email,picture.width(480).height(480)"])
graphRequest.startWithCompletionHandler({ (connection, result, error) -> Void in
if ((error) != nil)
{
println("Error: \(error)")
ProgressHUD.showError("Error occurred.")
} else {
self.userFullName = result.valueForKey("name") as? String
println("User Name is: \(self.userFullName)")
self.userEmail = result.valueForKey("email") as? String
println("User Email is: \(self.userEmail)")
// Here I try to add the retrieved Facebook data to the PFUser object
user["fullname"] = self.userFullName
user.email = self.userEmail
self.performSegueWithIdentifier("login", sender: self)
println("User signed up and logged in through Facebook!")
ProgressHUD.showSuccess("Welcome \(self.userFullName)!")
}
})
} else {
println("User logged in through Facebook!")
ProgressHUD.showSuccess("Welcome \(self.userFullName)!")
self.performSegueWithIdentifier("login", sender: self)
}
} else {
println("User cancelled the Facebook login.")
}
})
}
Thanks! Any help is greatly appreciated.
Save the user after you add the new data inside the Facebook request closure.
user.saveEventually()
Or any other save that you prefer.