Can't pinpoint FBSDKGraphRequest crash - facebook

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?

Related

Retrieving user email, using FBSDKLoginKit, from an account registered with phone number only: what does "email" result returns?

I'm following iOS Academy tutorial on a Chat App. In said app, when a user logs in for the first time using Facebook, i need to retrieve the associated email and store it in my database. Lurking at YT comments ( here ) i've found out that a FB account can have no associated email if it was registered with the phone number. Quoting the literal comment:
I created my facebook account without an email (only through a phone number) it still registers the authentication however the identifier is missing. It also does not send out information to the database. Incase anyone who has only a phone has tried.
Since i need to pass the retrieved email to my Firebase Realtime database, i want to handle this issue, but i would need to know what happens when i try to retrieve an email using FBSKDLoginKit API if there is no associated email.
Since my FB has an associated email, and apparently there's no way to remove it and leave the field blank, i tried to register a new FB account with my phone number. The problem is that in order to be able to run the app on test mode and log into FB, i would need to validate it on Facebook For Developers, but to log into Developers i need an associated email. So i'm at a dead end and can't test myself.
My question is: does anyone knows what the email result returns in a FB request if there is no associated email?
Here's my code, my guess is that the function hits the return in the guard block at the commented line down below, because the email is equal to nil, but from what i've read on Youtube it seems that only the Database block is skipped while the Firebase authentication succeeds. So maybe it returns an empty string, or something else.
func loginButton(_ loginButton: FBLoginButton, didCompleteWith result: LoginManagerLoginResult?, error: Error?) {
guard let token = result?.token?.tokenString else {
return
}
let facebookRequest = FBSDKLoginKit.GraphRequest(graphPath: "me",
parameters: ["fields" : "email, first_name"], tokenString: token,
version: nil,
httpMethod: .get)
facebookRequest.start { [weak self] _, result, error in
guard let result = (result as? [String: Any]), error == nil else {
self?.alertUserLoginError(message: "An error occurred while processing your request. Please try to sign in using Google.")
return
}
guard let firstName = (result["first_name"] as? String),
let email = (result["email"] as? String) else {
return
}
// Imo here the func returns cause email is equal to nil, but apparently that's not true.
// The database block is skipped, but the auth block down below is not.
DatabaseManager.shared.insertUser(with: chatUser, completion: { success in
// Doing other unrelated stuff
}
// The following block is run apparently, not sure why
let credential = FacebookAuthProvider.credential(withAccessToken: token)
FirebaseAuth.Auth.auth().signIn(with: credential) { authResult, error in
guard authResult != nil, error == nil else {
FBSDKLoginKit.LoginManager().logOut()
return
}
}
}
}
This is the database function in DatabaseManager class, called up above:
public func insertUser(with user: ChatAppUser, completion: #escaping (Bool) -> Void) {
database.child(user.safeEmail).setValue([
"first_name" : user.firstName
],
withCompletionBlock: { error, _ in
guard error == nil else {
completion(false)
return
}
completion(true)
})
}
func FacebookGETDataClicked(_ sender: Any)
{
let fbLoginManager : LoginManager = LoginManager()
fbLoginManager.logIn(permissions: ["email"], from: self) { (result, error) in
if (error == nil){
let fbloginresult : LoginManagerLoginResult = result!
let fbloginresultsss: Set<String> = fbloginresult.grantedPermissions
let arr = [String](fbloginresultsss)
if arr.count > 0 {
if(arr.contains("email"))
{
self.getFBUserData()
fbLoginManager.logOut()
}
}
}
}
}
func getFBUserData(){
var userProfileImage = String()
var useremail = String()
var userFullName = String()
var userID = String()
if((AccessToken.current) != nil){
GraphRequest(graphPath: "me", parameters: ["fields": "id, name, first_name,last_name, picture.type(large), email"]).start(completionHandler: { (connection, result, error) -> Void in
if (error == nil){
let dict = result as! [String : AnyObject]
print(dict)
if let mail = (dict["email"] as? String)
{
useremail = mail
}
if let name = (dict["name"] as? String)
{
userFullName = name
}
if let id = (dict["id"] as? String)
{
userID = id
}
if let pic = dict["picture"] as? NSDictionary
{
let profilePictureObj = pic
let datas = profilePictureObj["data"] as! NSDictionary
userProfileImage = datas["url"] as! String
}
print(userID,useremail,userFullName,userProfileImage)
}
})
}
}

Swift - Firebase Getting a Facebook Friends UID in Firestore

For a little background - I have a 'Follow Facebook Friends' ViewController where users can view their friends who also have downloaded my app to follow on the app.
I have correctly called the FBSDK to get a list of friends that have also downloaded the app, but now I need to get these friends uid's in Firebase so I can follow/unfollow them accordingly.
Currently, when I get a FB users id - I get the numeric string as mentioned in the facebook documentation. However, the users uid in Firebase is different. Is there a way to find which firebase user correlates with a FB id? Or should I try to store a Facebook User by facebook id in firebase?
var facebookContacts = [User]()
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "facebookContactCell", for: indexPath) as! FacebookContactCell
// Configure the cell...
let user = facebookContacts[indexPath.row]
cell.contactNameLabel.text = user.name
cell.userId = user.documentId
// Set follow button state
if followingArray.contains(cell.userId) {
cell.followButton.isSelected = true
} else {
cell.followButton.isSelected = false
}
let userImageRef = storage.child("userImages/"+(user.documentId)+"/profile_pic.jpg")
// Download in memory with a maximum allowed size of 1MB (1 * 1024 * 1024 bytes)
userImageRef.getData(maxSize: 1 * 1024 * 1024) { (data, error) in
if let error = error {
// Uh-oh, an error occurred! Display Default image
print("Error - unable to download image: \(error)")
cell.contactImageView.image = UIImage(named: "userProfileGray")
} else {
// Data for "locationImages/(locationId).jpg" is returned
cell.contactImageView.image = UIImage(data: data!)
}
SVProgressHUD.dismiss()
}
return cell
}
func getFbFriends() {
let params = ["fields": "id, first_name, last_name, name, email, picture"]
let graphRequest = FBSDKGraphRequest(graphPath: "/me/friends", parameters: params)
let connection = FBSDKGraphRequestConnection()
connection.add(graphRequest, completionHandler: { (connection, result, error) in
if error == nil {
let resultdict = result as! NSDictionary
print("Result Dict: \(resultdict)")
let data : NSArray = resultdict.object(forKey: "data") as! NSArray
for i in 0..<data.count {
let valueDict : NSDictionary = data[i] as! NSDictionary
let id = valueDict.object(forKey: "id") as! String
print("the id value is \(id)")
let name = valueDict.object(forKey: "name") as! String
print("the name value is \(name)")
let picDict = valueDict["picture"] as! NSDictionary
let picData = picDict["data"] as! NSDictionary
let picURL = picData.object(forKey: "url") as! String
print("the url value is \(picURL)")
self.facebookContacts.append(User(name: name, documentId: id))
}
DispatchQueue.main.async {
self.tableView.reloadData()
}
let friends = resultdict.object(forKey: "data") as! NSArray
print("Found \(friends.count) friends")
} else {
print("Error Getting Friends \(String(describing: error))");
}
})
connection.start()
}
I do not think that you can get the UID of your user based on their Facebook ID. It seems that you have two possibilities:
Search by email. This should allow you to pinpoint the person you are looking for.
Implement custom Facebook authentication. It requires a little extra work but is not too difficult to do.

Variable value becomes nil after fbsdk graph request

I am writing a project in swift and I am pretty new to the language.
I set a variable "username"'s value to the result of my request to the facebook sdk, but my variable value becomes nil after the graph request. The variable assignment works fine inside the graph request itself.
Here is my code.
import UIKit
import FBSDKCoreKit
import FBSDKLoginKit
import FBSDKShareKit
class SecondViewController: UIViewController {
var username : String = "X"
let label = UILabel()
override func viewDidLoad() {
super.viewDidLoad()
FBSDKGraphRequest(graphPath: "/me", parameters: ["Fields" : "name"]).start {
(connection, result, err) in
if err != nil {
print("Failed to start request", err)
return
}
var res = result as? [String : Any]
self.username = (res?["name"] as! String)
print(self.username)
}
print(self.username)
}
}
The first print statement that is inside the request works just fine but the second one that is outside of the request prints an empty line.
This was also the case when I wanted to set the text of a label to the value of the username variable. It worked inside the request but didn't do anything otherwise.
i think first perform login which permissions:
loginManager.logIn(withReadPermissions: ["email","public_profile"], from: self) { (FBSDKLoginManagerLoginResult, Error) in
if(Error == nil){
if(FBSDKLoginManagerLoginResult?.isCancelled)!{
SVProgressHUD.showError(withStatus: "You Cancelled To Login With Facebook, \n Try Again Later!")
}else if(FBSDKLoginManagerLoginResult?.token != nil){
SVProgressHUD.showSuccess(withStatus: "Login Successfull")
SVProgressHUD.show(withStatus: loadingText)
if((FBSDKAccessToken.current()) != nil){
FBSDKGraphRequest(graphPath: "me", parameters: ["fields": "id, name, first_name, last_name, email"]).start(completionHandler: { (connection, result, error) -> Void in
if (error == nil){
let resultDic = result as! [String: Any]
let facebookProfileUrl : String! = "http://graph.facebook.com/\((FBSDKLoginManagerLoginResult?.token!.userID)!)/picture?type=large"
var fbId = (FBSDKLoginManagerLoginResult?.token!.userID)! as String
var name = resultDic["name"] as! String
var email = resultDic["email"] as! String
}else{
print(error!.localizedDescription)
}
})
}
}
}
}
as your inside code is working which means you perform above well, i think its working in threading, which means your bottom "print(self.username)" is printed first, then above one which is inside of block,
you can LOG it after completion of Graph API response.

Get Facebook profile picture from URL

I want to upload the profile picture from Facebook to Firebase. I tried this answer: Upload Facebook image URL to Firebase Storage
However, Swift is giving me errors on the third line of code of that answer. The code is:
let dictionary = result as? NSDictionary
let data = dictionary?.object(forKey: "data")
let urlPic = (data?.objectForKey("url"))! as! String
Swift is telling me: Cannot call value of non-function type 'Any?!' after I changed the code to what Swift keeps suggesting me:
let urlPic = ((data as AnyObject).object(ForKey: "url"))! as! String
What is the code to use when I want to retrieve the profile picture from Facebook? My goal is to also store it into Firebase, but that will come after I get the profile picture first.
The answer is in Swift 1.2
I took reference here and implemented also
You can do this:
// accessToken is your Facebook id
func returnUserProfileImage(accessToken: NSString)
{
var userID = accessToken as NSString
var facebookProfileUrl = NSURL(string: "http://graph.facebook.com/\(userID)/picture?type=large")
if let data = NSData(contentsOfURL: facebookProfileUrl!) {
imageProfile.image = UIImage(data: data)
}
}
This is the way I got Facebook id:
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)")
if let id: NSString = result.valueForKey("id") as? NSString {
println("ID is: \(id)")
self.returnUserProfileImage(id)
} else {
println("ID es null")
}
}
})
}

Retrieve email FacebookSDK and Swift

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;
}
}
}];