How to retrieve user mail and name? (PFFacebookUtils.logInInBackgroundWithReadPermissions, swift 1.2) - facebook

I have successfully logged my user in, using the following code and plenty of reading on stackoverflow:
#IBAction func FBlogin(sender: UIButton) {
self.activity.startAnimating()
let permissions = ["public_profile", "email"]
PFFacebookUtils.logInInBackgroundWithReadPermissions(permissions) {
(user: PFUser?, error: NSError?) -> Void in
if let user = user {
self.activity.stopAnimating()
if user.isNew {
print(user.email)
print(user.username)
print("User signed up and logged in through Facebook!")
} else {
print(user.email)
print(user.username)
print("User logged in through Facebook!")
}
} else {
self.activity.stopAnimating()
}
}
}
This creates a new user in parse.com BUT does not fill-in email, username ...?
QUESTION: how does one retrieve the email and userName details?
AFTER searching and fiddling more I added this code:
func getUserDataFromFacebookProfile(user: PFUser)
{
var facebookid : String?
var username : String?
var userEmail : String?
var userGender : String?
var userLocation : String?
let graphRequest : FBSDKGraphRequest = FBSDKGraphRequest(graphPath: "me", parameters: nil)
graphRequest.startWithCompletionHandler({ (connection, result, error) -> Void in
if ((error) != nil)
{
// Process error
print("graphrequest error: \(error)")
}
else
{
//if this works, we can store user name und PFUser and mail in PFuser....
//not working yet
/*
id
name
first_name
last_name
link
gender
locale
timezone
updated_time
verified
*/
facebookid = result.valueForKey("id") as? String
userEmail = result.valueForKey("email") as? String
username = result.valueForKey("name") as? String
let fname = result.valueForKey("first_name") as? String
let lname = result.valueForKey("last_name") as? String
let loc = result.valueForKey("locale") as? String
let gend = result.valueForKey("gender") as? String
//userGender = result.valueForKey("gender") as? String
userLocation = result.valueForKey("location") as? String
print("*****************************************************************************")
print("")
print("graphrequest result \(facebookid), \(username), \(userEmail), \(gend), \(fname), \(lname)")
print("")
print("*****************************************************************************")
}
//persist in our DB for later
let thisUser: PFUser = user
print("updating current user")
if let uName = username {
thisUser.username = uName
}
if let uEmail = userEmail {
thisUser.email = uEmail
}
thisUser.saveInBackground()
})
}
The strange thing is that I can only retrieve id, email and name?!

About email: Search for "Declarative Fields" in the changelog: https://developers.facebook.com/docs/apps/changelog#v2_4
About username: That field is no longer available since v2.0 of the Graph API: https://developers.facebook.com/docs/apps/changelog#v2_0

So here is what worked for me. Appparently FB has again changed a few things. The key is to add the required fields to the parameter list, like so:
let graphRequest : FBSDKGraphRequest = FBSDKGraphRequest(graphPath: "me", parameters: ["fields": "email,first_name,last_name,gender,picture.width(480).height(480)"])

Related

Must be a non-empty string and not contain '.' '#' '$' '[' or ']''

Please help! I am experiencing an app crash.
public enum memberships {
case noMembership
case paid
case freeTrial
case trialExpired
}
public class DataManager {
private static let uuid = UIDevice.current.identifierForVendor!.uuidString
private static let user = Auth.auth().currentUser
private static let userRef = Database.database().reference().child("Users").child(user?.uid ?? "")
static var currentStatus: memberships? = nil
/**
Get's the current user's info from Firebase
and returns the info as a User object
*/
class func getUser(completion: #escaping (User?) -> ()) {
userRef.observeSingleEvent(of: .value, with: { (snapshot) in
if let value = snapshot.value as? [String: Any] {
// UIPasteboard.general.string = uuid
let uuid = snapshot.key
let name = value["name"] as? String ?? ""
let email = value["email"] as? String ?? ""
let dateJoined = value["dateJoined"] as? String ?? ""
let membershipString = value["membership"] as? String
let alertsStartTime = value["alertsStartTime"] as? String ?? ""
let alertsEndTime = value["alertsEndTime"] as? String ?? ""
let alertsFrequency = value["alertsFrequency"] as? Int ?? 1
let alertsType = value["alertsType"] as? String ?? ""
let isEnable = value["isEnable"] as? Bool ?? true
//Gets users current membership
var membershipStatus: memberships!
if membershipString == "Paid" {
membershipStatus = .paid
}else if membershipString == "NoMembership" {
membershipStatus = .noMembership
}else{
membershipStatus = Utils.getUserMembershipStatus(dateJoined: dateJoined)
}
let user = User(uuid: uuid, name: name, email: email, dateJoined: dateJoined, membership: membershipStatus, alertsStartTime: alertsStartTime, alertsEndTime: alertsEndTime, alertsType: alertsType, alertsFrequency: alertsFrequency, isEnable: isEnable)
completion(user)
}else{
completion(nil)
}
}) { (error) in
print(error.localizedDescription)
completion(nil)
}
}
Your user object is empty. So user?.uid is nil. Which means child(user?.uid ?? "") -> child("").
Firebase does not accept empty strings as key values (It also does not accepts strings which includes '.' '#' '$' '[' or ']'' as keys).
So in your case make sure user is logged or use different key value.

SwiftUI: Value of type 'DocumentSnapshot?' has no member 'document'

When trying to fetch a user, I am getting the error: "Value of type 'DocumentSnapshot?' has no member 'document'". The error occurs on the line that starts "let uid....".
import SwiftUI
import Firebase
// Global Refernce
let ref = Firestore.firestore()
func fetchUser(uid: String,completion: #escaping (UserModel) -> ()){
ref.collection("Users").document(uid).getDocument { (doc, err) in
guard let user = doc else{return}
let username = user.data()?["username"] as? String ?? "No Username"
let pic = user.data()?["imageurl"] as? String ?? "No image URL"
let bio = user.data()?["bio"] as? String ?? "No bio"
let uid = doc.document.data()["uid"] as! String ??
DispatchQueue.main.async {
completion(UserModel(username: username, pic: pic, bio: bio, uid: uid))
}
}
}
if uid is a field like others username and imageurl then use
let uid = user.data()["uid"] as? String ?? ""
if you have made that uid as id of the document then use
let uid = doc.documentID
See Docs

How to fetch limited amount of users from Firebase

I am doing a card stack swipe app with users similar to Tinder.
I want to fetch a batch of 10 users from Firebase. I have used queryLimited(toFirst: 10) for this but later in my function i won't fetch the users who have already been accepted(swiped right). This means that if the first 10 users from Firebase already have been accepted nobody will be fetched. I want to fetch the first 10 users who hasn't been accepted.
Does anyone have a great solution for this?
Thank you.
FetchUsers code for fetching users from Firebase:
func fetchAllUsers(completion: #escaping (_ message: String) -> Void){
//User or advertiser?
Database.database().reference(withPath: "Advertiser").child(uid).observeSingleEvent(of: .value, with: { (snapshot) in
if snapshot.exists(){
myAdvertiserVar.advertiser = true
self.currentUserKind = "Advertiser"
self.otherUserKind = "Users"
}
else{
myAdvertiserVar.advertiser = false
self.currentUserKind = "Users"
self.otherUserKind = "Advertiser"
}
// Fetch
let query = self.ref?.child(self.otherUserKind).queryOrdered(byChild: "email").queryLimited(toFirst: 10)
query?.observeSingleEvent(of: .value) {
(snapshot) in
let g = DispatchGroup()
for child in snapshot.children.allObjects as! [DataSnapshot] {
let id = child.key
//If Already Accepted, don't fetch
g.enter()
Database.database().reference(withPath: self.currentUserKind).child(self.uid).child("Accepted").child(id).observeSingleEvent(of: .value, with: {(accepted) in
if accepted.exists(){
print("\(id) är redan Accepted")
}
else{
if myAdvertiserVar.advertiser == true{
let value = child.value as? NSDictionary
let username = value?["Username"] as? String
let occupation = value?["Occupation"] as? String
let age = value?["Age"] as? String
let bio = value?["Bio"] as? String
let email = value?["email"] as? String
let user = User(id: id, username: username, occupation: occupation, age: age, bio: bio, email: email)
self.usersArray.append(user)
}
else{
let value = child.value as? NSDictionary
let username = value?["Owner"] as? String
let occupation = value?["Location"] as? String
let age = value?["Rent"] as? String
let bio = value?["About"] as? String
let email = value?["email"] as? String
let user = User(id: id, username: username, occupation: occupation, age: age, bio: bio, email: email)
self.usersArray.append(user)
}
}
g.leave()
})
}
g.notify(queue: .main, execute: {
print(self.usersArray.count)
completion("Users list fetched")
})
}
})
}

Login details remains after user logout swift using Token authentication

I have an application that uses rest framework to provide API. I have the application all setup but I have a challenge in that when a user login the user's details is gotten. login is achieved via token authentication and this is stored in UserDefaults when the logout button is clicked the user details is removed from the device and login in with another user is supposed to be displayed on login i.e the logged in user,s email and username but instead it shows the initial logged in user,s details and no matter how much I try it it never displays the current logged in users details but instead it displays the initial users details.
My login code is
let defaults = UserDefaults.standard
var isLoggedIn : Bool {
get {
return defaults.bool(forKey: LOGGED_IN_KEY)
}
set {
defaults.set(newValue, forKey: LOGGED_IN_KEY)
}
}
var authToken: String {
get {
return defaults.value(forKey: TOKEN_KEY) as? String ?? ""
}
set {
defaults.set(newValue, forKey: TOKEN_KEY)
}
}
var userUsername: String {
get {
return defaults.value(forKey: USER_USERNAME) as? String ?? ""
}
set {
defaults.set(newValue, forKey: USER_USERNAME)
}
}
//MARK :- LOGGIN
func findUserByUserName(completion: #escaping CompletionHandler) -> Void {
Alamofire.request(URL_USER_BY_USERNAME, method: .get, parameters: nil, encoding: JSONEncoding.default, headers: TOKEN_HEADER).validate().responseJSON { (response) in
print("URL USER BY HEADER \(self.authToken)")
if response.result.error == nil {
guard let data = response.data else {return}
let jsonString = String(data: data, encoding: .utf8)
print(jsonString as Any)
self.setUserInfo(data: data)
completion(true)
}
else {
completion(false)
debugPrint("ERROR 22222\(response.result.error as Any)")
}
}
}
func setUserInfo(data: Data) -> Void {
do {
let json = try JSON(data: data)
let pk = json["pk"].intValue
let username = json["username"].stringValue
let email = json["email"].stringValue
let firstName = json["first_name"].stringValue
let lastName = json["last_nameme"].stringValue
print("THE USERNAME IZZZZ \(username)")
UserDataService.instance.setUserData(pk: pk, username: username, email: email, firstName: firstName, lastName: lastName)
} catch {
print(error)
}
}
UserDetails
class UserDataService {
static let instance = UserDataService()
public private(set) var pk = 0
public private(set) var username = ""
public private(set) var email = ""
public private(set) var firstName = ""
public private(set) var lastName = ""
func setUserData(pk: Int, username: String, email: String, firstName: String, lastName: String) -> Void {
self.pk = pk
self.username = username
self.email = email
self.firstName = firstName
self.lastName = lastName
}
func logoutUser() -> Void {
self.pk = 0
self.username = ""
self.email = ""
self.firstName = ""
self.lastName = ""
AuthService.instance.isLoggedIn = false
AuthService.instance.authToken = ""
AuthService.instance.userUsername = ""
}}
For my logout I have
#IBAction func logoutPressed(_ sender: Any) {
UserDataService.instance.logoutUser()
NotificationCenter.default.post(name: NOTIFY_USER_DATA_DID_CHANGE, object: nil)
dismiss(animated: true, completion: nil)
}
Part of login
func setupUserInfo() {
if AuthService.instance.isLoggedIn {
loginBtn.setTitle(UserDataService.instance.username, for: .normal)
} else {
loginBtn.setTitle("Login", for: .normal)
userImg.image = UIImage(named: "menuProfileIcon")
userImg.backgroundColor = UIColor.clear
tableView.reloadData()
}
}
Thanks in advance and further codes would be supplied on request

How to use FIRApp.createUserWithEmail in specific database reference?

Iv been converting my post Firebase 2 codebase to Firebase 3 and having some troubles.
So basically Im trying to figure out how I create a new user at a specific location on my Firebase DB.
Goal - I want to save all new users # var _USER_REF = FIRDatabaseReference().child("\(BASE_URL)/users")
Here is the code so far.
class DataService {
static let dataService = DataService()
let BASE_URL = "https://project-1321.firebaseio.com"
var _BASE_REF = FIRDatabaseReference().child(BASE_URL)
var _USER_REF = FIRDatabaseReference().child("\(BASE_URL)/users")
var _NEWS_REF = FIRDatabaseReference().child("\(BASE_URL)/news")
var _MARKET_STATS = FIRDatabaseReference().child("\(BASE_URL)/market")
var CURRENT_USER_REF: FIRDatabaseReference {
let userID = NSUserDefaults.standardUserDefaults().valueForKey("uid") as! String
let currentUser = FIRDatabaseReference().child("\(_BASE_REF)").child("users").child(userID)
//let currentUser = Firebase(url: "\(BASE_REF)").childByAppendingPath("users").childByAppendingPath(userID)
return currentUser
}
func createNewAccount(uid: String, user: Dictionary<String, String>) {
_USER_REF.child(uid).setValue(user)
}
}
View Controller
#IBAction func registerAccount(sender: AnyObject) {
guard let email = self.emailRegField.text where !self.emailRegField.text!.isEmpty else {
return
}
guard let username = self.usernameRegField.text where !self.usernameRegField.text!.isEmpty else {
return
}
guard let password = self.passwordRegField.text where !self.passwordRegField.text!.isEmpty else {
return
}
FIRAuth.auth()?.createUserWithEmail(email, password: password) {
(user, error) in
if error != nil {
print(error)
self.signUpErrorAlert("Alert", message: "There was a problem signing up!")
} else {
let user = ["provider": user?.providerID, "email": email, "username": username]
DataService.createNewAccount(user) // Doesnt Work
}
//Store UID in NSDefaults so if user reopen app they automatically log in if UID exsisits.
NSUserDefaults.standardUserDefaults().setValue(result ["uid"], forKey: "uid")
// Segue New User
self.performSegueWithIdentifier("newUserSegue", sender: nil)
}
// Loggin in a User who already has UID Saved to NSDefaults
When a user log's in or Registers I plan to save their "UID" to NSDefaults.
Then check like so :
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
if NSUserDefaults.standardUserDefaults().valueForKey("uid") != nil {
self.performSegueWithIdentifier("newUserSegue", sender: nil)
} else {
print("User is not registered or has their UID saved to NSDefaults")
}
}
Is this a safe method?
#brkr In response to your comment above, you can still use the UIDs in order to add unique users to your Firebase database.
For example, here is your users ref (in the DataService Class):
let REF_USERS = FIRDatabase.database().referenceFromURL("\(URL_BASE)/users")
Now, create a user in Firebase 3.0:
FIRAuth.auth()!.createUserWithEmail(email, password: pwd, completion: { authData, error in
if error == nil {
// Log user in
FIRAuth.auth()?.signInWithEmail(email, password: pwd) { authData, error in
// Save user information to Firebase data
let user = // your user Dictionary
DataService.createNewAccount(authData!.uid, user: user)
}
} else {
// Handle login error here
}
})
The code for the create account method:
func createNewAccount(uid: String, user: Dictionary<String, String>) {
REF_USERS.child(uid).setValue(user)
}
I dont think it is necessary with new Firebase, look in the Dashboard, all your users should be under "Auth" tab,
Also this line doesnt make any sense in new Firebase, the URL you are querying is in the .plist you downloaded.
let BASE_URL = "https://project-1321.firebaseio.com" //remove this line
and use something like this
let firebaseRef = FIRDatabase.database().reference()
let newsRef = firebaseRef.child("news")
you can find many useful informations here https://firebase.google.com/docs/auth/ios/password-auth#sign_in_a_user_with_an_email_address_and_password