Im using the latest version for Facebook SDK 4.15 and and Google Firebase to authenticate users. I'm following along the documentation here: https://firebase.google.com/docs/auth/ios/facebook-login
This is working for me.
However I would like to pull extra permissions like email from the Facebook user. I have modified the code like so:
#IBOutlet var facebookButton : FBSDKLoginButton!
override func viewDidLoad() {
/...
facebookButton.readPermissions = ["email","public_profile"]
...
}
With this I get the Facebook confirm dialog asking for email permission when authorizing my app through Facebook. So that works.
However on my callback the user's email address is always nil:
func loginButton(loginButton: FBSDKLoginButton!, didCompleteWithResult result: FBSDKLoginManagerLoginResult!, error: NSError?) {
if let error = error {
print(error.localizedDescription)
return
}
let credential = FIRFacebookAuthProvider.credentialWithAccessToken(FBSDKAccessToken.currentAccessToken().tokenString)
FIRAuth.auth()?.signInWithCredential(credential) { (user, error) in
// ...
if let error = error {
print(error.localizedDescription)
return
}
print("user.uid \(user!.uid)")
print("user.photoURL \(user!.photoURL)")
print("user.providerID \(user!.providerID)")
print("user.email \(user!.email)")
print("user.displayName \(user!.displayName)")
How can I pull the extra permission like email through Facebook login while using Google Firebase Authentication?
When using third party authentication, you get back an authentication token which you then use to sign a user into your app. With this token, Firebase Auth is able to extract the user's email address along with it. But for your case, if you're looking to getting the user's email address after FB logging in, you'd need to use Facebook's Graph API. You simply have to make a GraphRequest containing the info you're asking for as shown below.
Swift 2
struct FacebookPermission
{
static let ID: String = "id"
static let NAME: String = "name"
static let EMAIL: String = "email"
static let PROFILE_PIC: String = "picture"
static let LAST_NAME: String = "last_name"
static let FIRST_NAME: String = "first_name"
static let USER_FRIENDS: String = "user_friends"
static let PUBLIC_PROFILE: String = "public_profile"
}
func getFacebookData()
{
let graphRequest: FBSDKGraphRequest = FBSDKGraphRequest(graphPath: "me", parameters: ["fields" : "\(FacebookPermission.NAME), \(FacebookPermission.FIRST_NAME), \(FacebookPermission.LAST_NAME), \(FacebookPermission.EMAIL), \(FacebookPermission.PROFILE_PIC).type(large)"])
graphRequest.startWithCompletionHandler { (connection: FBSDKGraphRequestConnection!, result:AnyObject!, error: NSError!) in
if error == nil
{
if let userName = result.valueForKey(FacebookPermission.NAME) as? String
{
self.currentUser.userName = userName
}
if let firstName = result.valueForKey(FacebookPermission.FIRST_NAME) as? String
{
self.currentUser.firstName = firstName
}
if let lastName = result.valueForKey(FacebookPermission.LAST_NAME) as? String
{
self.currentUser.lastName = lastName
}
if let email = result.valueForKey(FacebookPermission.EMAIL) as? String
{
self.currentUser.email = email
}
if let profilePic = result.valueForKey(FacebookPermission.PROFILE_PIC)
{
let facebookImageURL = profilePic.valueForKey("data")?.valueForKey("url") as? String
if let unwrappedURL = facebookImageURL
{
let imageData: NSData = NSData(contentsOfURL: NSURL(string: unwrappedURL)!)!
self.currentUser.profileImage = UIImage(data: imageData)!
}
}
}
else
{
print("Facebook Graph Request Error")
}
}
}
Swift 3
func getFacebookData()
{
let graphRequest: FBSDKGraphRequest = FBSDKGraphRequest(graphPath: "me", parameters: ["fields" : "\(FacebookPermission.NAME), \(FacebookPermission.FIRST_NAME), \(FacebookPermission.LAST_NAME), \(FacebookPermission.EMAIL), \(FacebookPermission.PROFILE_PIC).type(large)"])
graphRequest.start { (connection: FBSDKGraphRequestConnection?, result: Any?, error: Error?) in
if error == nil
{
if let facebookData = result as? NSDictionary
{
if let userName = facebookData.value(forKey: FacebookPermission.NAME) as? String
{
self.currentUser.userName = userName
}
if let firstName = facebookData.value(forKey: FacebookPermission.FIRST_NAME) as? String
{
self.currentUser.firstName = firstName
}
if let lastName = facebookData.value(forKey: FacebookPermission.LAST_NAME) as? String
{
self.currentUser.lastName = lastName
}
if let email = facebookData.value(forKey: FacebookPermission.EMAIL) as? String
{
self.currentUser.email = email
}
if let profilePic = facebookData.value(forKey: FacebookPermission.PROFILE_PIC)
{
let facebookImageURL = ((profilePic as AnyObject).value(forKey: "data") as AnyObject).value(forKey: "url") as? String
if let unwrappedURL = facebookImageURL
{
let imageData: Data = try! Data(contentsOf: URL(string: unwrappedURL)!)
self.currentUser.profileImage = UIImage(data: imageData)!
}
}
}
}
else
{
print("Facebook Graph Request Error")
}
}
}
After grabbing their info you may go ahead and store it into your Firebase Database.
Here's more info about Facebook Permission.
Cheers mate!
I found this
Firebase only stores a list of email+password users. It doesn't store any data for users of your app that are signed with social providers (such as Facebook).
Related
I try to get user id, email, picture.type(large),updated_time from Facebook SDK, I successfully get all of them but I didn't get picture gives me:
Error Cannot call value of non-function type 'Any?!'
My clear codes under below.
import UIKit
import Foundation
class LoginViewController : UIViewController, FBSDKLoginButtonDelegate {
private var fromLogin = [Login]()
#IBOutlet weak var loginButton : FBSDKLoginButton!
override func viewDidLoad() {
super.viewDidLoad()
// Check Session
if let token = FBSDKAccessToken.current(){
print(token.tokenString! as Any)
self.fetchProfile()
}
loginButton.delegate = self
loginButton.readPermissions = ["email", "public_profile"]
}
// Get Profile
func fetchProfile() {
print("fetch profile")
// Create facebook graph with fields
FBSDKGraphRequest(graphPath: "me", parameters: ["fields":"id, name, email, picture.type(large), updated_time"]).start { (connection, result, error) in
// check error
if error != nil {
// error happen
print("Failed to start graph request: \(error?.localizedDescription)")
return
}
if let userDict = result as? NSDictionary {
let name = userDict["name"] as? String
let id = userDict["id"] as? String
let email = userDict["email"] as? String
// HERE GIVES ERROR I DIDNT GET PICTURE WITH STRING
let userPicture = userDict.objectForKey("picture")?.objectForKey("data")?.objectForKey("url") as String
print(userPicture)
print(name as Any)
print(id as Any)
print(email as Any)
}
}
}
// Delegate Method
func loginButton(_ loginButton: FBSDKLoginButton!, didCompleteWith result: FBSDKLoginManagerLoginResult!, error: Error!){
// check error
if error != nil {
// error happen
print(error?.localizedDescription as Any)
return
}
print("Successfull login in with facebook...")
fetchProfile()
}
func loginButtonDidLogOut(_ loginButton: FBSDKLoginButton!){
print("Log Out")
}
This line gives error;
let userPicture = userDict.objectForKey("picture")?.objectForKey("data")?.objectForKey("url") as String
Also picture object json data example here;
picture = {
data = {
"is_silhouette" = 0;
url = "https://scontent.xx.fbcdn.net/v/t1.0-1/p200x200/14067524_9513823423422249533237851_n.jpg?oh=88eb9b80abbb2342346c01de298&oe=58C5138B";
};
};
I'm using Xcode 8.1 and Swift 3.0
This requires simple parsing .. "picture" is Dictionary and so is "data". so something like below works for you
guard let userDict = result as? [String:Any] else { return }
let name = userDict["name"] as? String
let id = userDict["id"] as? String
let email = userDict["email"] as? String
if let picture = userDict["picture"] as? [String:Any] ,
let imgData = picture["data"] as? [String:Any] ,
let imgUrl = imgData["url"] as? String {
print(imgUrl)
}
Replace this
FBSDKGraphRequest(graphPath: "me", parameters: ["fields":"id, name, email, picture.type(large), updated_time"]).start { (connection, result, error) in
with
FBSDKGraphRequest(graphPath: "me", parameters: ["fields":"id, name, email, picture.width(500).height(500), updated_time"]).start { (connection, result, error) in
Then check this
if let imageUrl = ((dictData.value(forKey: "picture") as? NSDictionary)?.value(forKey: "data") as? NSDictionary)?.value(forKey: "url") as? NSString
{
Facebook.imagesArr.add(imageUrl)
print("Pictureasd:",Facebook.imagesArr)
}
func loginButton(_ loginButton: FBSDKLoginButton!, didCompleteWith result: FBSDKLoginManagerLoginResult!, error: Error!)
{
FBSDKGraphRequest.init(graphPath: "me", parameters: ["fields":"first_name, last_name, picture.type(large)"]).start { (connection, result, error) -> Void in
let strFirstName: String = (result.object(forKey: "first_name") as? String)!
let strLastName: String = (result.object(forKey: "last_name") as? String)!
let strPictureURL: String = (result.object(forKey: "picture")?.object(forKey: "data")?.object(forKey: "url") as? String)!
self.lblName.text = "Welcome, \(strFirstName) \(strLastName)"
self.ivUserProfileImage.image = UIImage(data: try! Data(contentsOf: URL(string: strPictureURL)!))
}
}
I'm trying to fetch the user's fb profile pic but wasn't able to do so far. I'm trying to do something simple: the user log in with fb account and the app goes to another view where appears his name, email and profile picture. User's name and email are okay, but I can't get the picture!
The app is crashing with my actual code because apparently I'm unwrapping a nil optional value, but I don't know why it's nil.
My code:
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
let graphRequest : FBSDKGraphRequest = FBSDKGraphRequest(graphPath: "me", parameters: ["fields":"id,email,name,picture.width(480).height(480)"])
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)")
let id = result.valueForKey("id") as! String
self.nameLabel.text = userName as String
self.emailLabel.text = userEmail as String
self.profilePic.image = self.getProfPic(id)
}
})
}
func getProfPic(fid: String) -> UIImage? {
if (fid != "") {
let imgURLString = "http://graph.facebook.com/" + fid + "/picture?type=large" //type=normal
let imgURL = NSURL(string: imgURLString)
let imageData = NSData(contentsOfURL: imgURL!)
let image = UIImage(data: imageData!) // CODE CRASHES IN HERE
return image
}
return nil
}
From your comments I understand that it crashes at the image assigning, you should be doing it with the conditional binding methodology of Swift (if let) in order to avoid unwrapping a nil optional value:
if let data = result["picture"]?["data"]
{
if let url = data["url"] as? String
{
profilePictureURL = url
}
}
Also, as you can see I am not using the valueForKey method.
I got my app onto the App Store. Everything was working fine on my end, and apparently on the reviewers end.
After the app went live, some users reported crashing immediately after they log in with Facebook. Here's the code for where I think it's crashing (run right after users log in with Facebook):
import UIKit
import FBSDKCoreKit
import FBSDKLoginKit
protocol getUserDataDelegate {
func gotData()
}
public var userEmailForMixpanel = ""
public var userNameForInvites = ""
class GetUserData: NSObject {
var facebookid:String = ""
var userEmail:String = ""
var userFirstName:String = ""
var userLastName: String = ""
var userGender:String = ""
var userBirthday:String = ""
var delegate = getUserDataDelegate?()
func returnUserData() {
let graphRequest : FBSDKGraphRequest = FBSDKGraphRequest(graphPath: "me", parameters: ["fields": "id, email, first_name, last_name, gender, birthday"])
graphRequest.startWithCompletionHandler({ (connection, result, error) -> Void in
if ((error) != nil)
{
print("Error: \(error)")
}
else
{
self.facebookid = (result.valueForKey("id") as? String)!
self.userEmail = (result.valueForKey("email") as? String)!
self.userFirstName = (result.valueForKey("first_name") as? String)!
self.userLastName = (result.valueForKey("last_name") as? String)!
self.userGender = (result.valueForKey("gender") as? String)!
//self.userBirthday = (result.valueForKey("birthday") as? String)!
userEmailForMixpanel = self.userEmail
userNameForInvites = self.userFirstName
NSUserDefaults.standardUserDefaults().setValue(userEmailForMixpanel, forKey: "userEmail")
NSUserDefaults.standardUserDefaults().setValue(userNameForInvites, forKey: "userName")
NSUserDefaults.standardUserDefaults().synchronize()
Mixpanel.sharedInstanceWithToken("abdc")
let mixpanel = Mixpanel.sharedInstance()
mixpanel.registerSuperProperties(["Gender":self.userGender])
print(self.facebookid)
print(self.userEmail)
print(self.userFirstName)
print(self.userLastName)
print(self.userGender)
//print(self.userBirthday)
self.checkIfUserExists()
}
})
}
func checkIfUserExists() {
showTutorial = true
let url:NSURL = NSURL(string: "url")!
let task:NSURLSessionDataTask = NSURLSession.sharedSession().dataTaskWithURL(url) { (data:NSData?, response:NSURLResponse?, error:NSError?) -> Void in
let userTokenDataDictionary:NSDictionary = (try! NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers)) as! NSDictionary
if userTokenDataDictionary ["token"] != nil {
userAccessToken = (userTokenDataDictionary["token"] as? String)!
NSUserDefaults.standardUserDefaults().setValue(userAccessToken, forKey: "userAccessToken")
NSUserDefaults.standardUserDefaults().synchronize()
print("Token for Existing User:\(userAccessToken)")
self.finishedGettingData()
}
if userTokenDataDictionary ["error"] != nil {
userAccessToken = (userTokenDataDictionary["error"] as? String)!
print(userAccessToken)
print("User needs to be created")
self.createNewUserFromFacebook()
}
}
task.resume()
}
func createNewUserFromFacebook() {
let url:NSURL = NSURL(string: "url")!
print(url)
let task:NSURLSessionDataTask = NSURLSession.sharedSession().dataTaskWithURL(url) { (data:NSData?, response:NSURLResponse?, error:NSError?) -> Void in
let userTokenDataDictionary:NSDictionary = (try! NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers)) as! NSDictionary
if userTokenDataDictionary ["token"] != nil {
userAccessToken = (userTokenDataDictionary["token"] as? String)!
NSUserDefaults.standardUserDefaults().setValue(userAccessToken, forKey: "userAccessToken")
NSUserDefaults.standardUserDefaults().synchronize()
}
if userTokenDataDictionary ["error"] != nil {
userAccessToken = (userTokenDataDictionary["error"] as? String)!
print(userAccessToken)
}
print("Token for New User:\(userAccessToken)")
self.finishedGettingData()
}
task.resume()
}
func checkIfUserHasUsedListenerApp() {
let accessToken = NSUserDefaults.standardUserDefaults().stringForKey("userAccessToken")!
let url:NSURL = NSURL(string: "url")!
print(url)
let task:NSURLSessionDataTask = NSURLSession.sharedSession().dataTaskWithURL(url) { (data:NSData?, response:NSURLResponse?, error:NSError?) -> Void in
let adDataDict:NSDictionary = (try! NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers)) as! NSDictionary
if adDataDict ["used_ListenerApp"] != nil {
let responseCode = adDataDict.valueForKey("used_ListenerApp") as! Bool
print(responseCode)
if responseCode == false {
Mixpanel.sharedInstanceWithToken("abc")
let mixpanel = Mixpanel.sharedInstance()
mixpanel.track("New User Signed Up", properties: ["distinct_id":userEmailForMixpanel])
}
if responseCode == true {
return
}
}
}
task.resume()
}
func finishedGettingData() {
self.checkIfUserHasUsedListenerApp()
Mixpanel.sharedInstanceWithToken("abc")
let mixpanel = Mixpanel.sharedInstance()
mixpanel.track("User Logged In", properties: ["distinct_id":userEmailForMixpanel])
if let actualdelegate = self.delegate {
actualdelegate.gotData()
}
}
}
It's only crashing for some users, not all. I even tried creating a loop that generates a bunch of user data to run it through this code, and I couldn't replicate the crash.
Any advice would be appreciated a lot.
UPDATE
It looks like it has to do with Facebook not returning an email address. I'll keep looking though.
I figured it out. Part of the problem was that Facebook wasn't returning an email address for some users, so I checked for that and if it didn't return an email, I created one with their Facebook ID to get them through the login process (fbid#facebook.mywebsite.com).
Another part of the problem was that some users had logged into my website using an email address that was also assigned to their Facebook account, and tried logging in with Facebook on the app. I fixed this by having it merge their Facebook info with their existing account during the authorization process if an existing account is found.
I have been searching since few days on how I can get user details based on his/her Twitter account ,I'm using twitter login in my application
& I haven't found anything about this in Swift, so i'm asking!
How can I get the username & email & uprofile Image of a logged in User with Parse from Twitter in order to save them on parse cloud?
You can access the username and userID of the logged-in user pretty easily. Inside most Twitter login methods you'll see something like this:
#IBAction func loginTwitter(sender: UIBarButtonItem) {
Twitter.sharedInstance().logInWithCompletion {
(session, error) -> Void in
if (session != nil) {
print(session?.userName)
print(session?.userID)
} else {
print("error")
}
}
}
Twitter does not expose the email address of users as far as I'm aware.
For the profile image you'll need to send a GET request. Here is some code that may not be up to date with the latest TwitterKit version but should at least give you a sense of how the request should be formatted.
func getUserInfo(screenName : String){
if let userID = Twitter.sharedInstance().sessionStore.session()!.userID {
let client = TWTRAPIClient(userID: userID)
let url = "https://api.twitter.com/1.1/users/show.json"
let params = ["screen_name": screenName]
var clientError : NSError?
let request = Twitter.sharedInstance().APIClient.URLRequestWithMethod("GET", URL: url, parameters: params, error: &clientError)
client.sendTwitterRequest(request) { (response, data, connectionError) -> Void in
if let someData = data {
do {
let results = try NSJSONSerialization.JSONObjectWithData(someData, options: .AllowFragments) as! NSDictionary
print(results)
} catch {
}
}
}
}
}
You'll need to go through the JSON that gets returned and find "profile_image_url_https" a couple levels down.
Good Luck!
in Swift 4.2 and Xcode 10.1
It's getting email also.
import TwitterKit
#IBAction func onClickTwitterSignin(_ sender: UIButton) {
TWTRTwitter.sharedInstance().logIn { (session, error) in
if (session != nil) {
let name = session?.userName ?? ""
print(name)
print(session?.userID ?? "")
print(session?.authToken ?? "")
print(session?.authTokenSecret ?? "")
let client = TWTRAPIClient.withCurrentUser()
client.requestEmail { email, error in
if (email != nil) {
let recivedEmailID = email ?? ""
print(recivedEmailID)
}else {
print("error--: \(String(describing: error?.localizedDescription))");
}
}
//To get profile image url and screen name
let twitterClient = TWTRAPIClient(userID: session?.userID)
twitterClient.loadUser(withID: session?.userID ?? "") {(user, error) in
print(user?.profileImageURL ?? "")
print(user?.profileImageLargeURL ?? "")
print(user?.screenName ?? "")
}
let storyboard = self.storyboard?.instantiateViewController(withIdentifier: "SVC") as! SecondViewController
self.navigationController?.pushViewController(storyboard, animated: true)
}else {
print("error: \(String(describing: error?.localizedDescription))");
}
}
}
For logout
#IBAction func onClickTwitterSignout(_ sender: UIButton) {
let store = TWTRTwitter.sharedInstance().sessionStore
if let userID = store.session()?.userID {
print(store.session()?.userID ?? "")
store.logOutUserID(userID)
print(store.session()?.userID ?? "")
self.navigationController?.popToRootViewController(animated: true)
}
}
Can't get the user's name or email when login with Facebook via Parse. I should have set everything else properly in the AppDelegate.
When I login with my email, my User class works, and can use data I registered with. When I try to login via Facebook, I only got the long alphanumerical string as username and stop. I'd like to retrive name, foto, birth and city.
In my User.swift file I'got this:
import Foundation
struct User
{
let username : String
let address : String
}
This is my login button:
#IBAction func facebookLoginAction(sender: UIButton)
{
PFFacebookUtils.logInInBackgroundWithReadPermissions(["public_profile", "user_about_me", "user_birthday"]) {
(user: PFUser?, error: NSError?) -> Void in
if let user = user
{
if user.isNew
{
println("User signed up and logged in through Facebook!")
}
else
{
println("User logged in through Facebook!")
}
self.dismissViewControllerAnimated(true, completion: nil)
}
else
{
println("Uh oh. The user cancelled the Facebook login.")
}
}
}
tried this too, but doesn't work:
func getUserInfo() {
// if let session = PFFacebookUtils.session() {
if let session = PFFacebookUtils.facebookLoginManager() {
if session.isOpen {
println("session is open")
FBRequestConnection.startForMeWithCompletionHandler({ (connection: FBRequestConnection!, result: AnyObject!, error: NSError!) -> Void in
//println("done me request")
if error != nil {
println("facebook me request - error is not nil :(")
} else {
println("facebook me request - error is nil :) ")
let urlUserImg = "http://graph.facebook.com/\(result.objectID)/picture?type=large"
let firstName = result.first_name
let lastName = result.last_name
}
})
}
} else {
//let user:PFUser = PFUser.currentUser()
//println("ohooo \(user)")
}
}
thanks in advance
This is working code I use to get Facebook information with Parse. The function is called after successful authentication with user.isNew. It can also be called even if the user isn't new to make sure you have the most up-to-date information when they log back in.
func loadFacebookData() {
let graphRequest = FBSDKGraphRequest(graphPath: "me", parameters: ["fields": "email, name, id"])
graphRequest.startWithCompletionHandler { (connection, result, error) -> Void in
if error != nil {
let error = error!.userInfo["error"] as! String
}
else {
if let userName = result.valueForKey("name") as? String, email = result.valueForKey("email") as? String, id = result.valueForKey("id") as? String {
let pictureURL: NSURL = NSURL(string: "https://graph.facebook.com/\(id)/picture?type=large&return_ssl_resources=1")!
let user = PFUser.currentUser()!
let query = PFUser.query()
query!.whereKey("email", equalTo: email)
query!.getFirstObjectInBackgroundWithBlock({ (oldUser: PFObject?, error) -> Void in
if error != nil && oldUser != nil {
let error = error!.userInfo["error"] as! String
}
else {
self.setFacebookInfo(user, userEmail: email, userName: userName, pictureURL: pictureURL)
}
})
}
}
}
}