Attempting to save username from twitter user to Firebase database iOS app - swift

I'm attempting to save a twitter users username into the database for later reference my code below is executing but doesn't seem to be accessing the database or saving the username into the database and I'm really lost as to why. I'm attempting to have the username and userID so I can retrieve information about the user for a profile page in the app. So if I can avoid saving this data to the database all together that works too but I don't think it can be done that way.
fileprivate func setupTwitterButton() {
let twitterButton = TWTRLogInButton { (session, error) in
if let err = error {
print("Failed to login via Twitter: ", err)
return
}
// debug statement
//print("Successfully logged in using Twitter")
HUD.show(.labeledProgress(title: nil, subtitle: "Signing In"))
//we've authenticated twitter, time to log into firebase
guard let token = session?.authToken else { return }
guard let secret = session?.authTokenSecret else { return }
let creds = FIRTwitterAuthProvider.credential(withToken: token, secret: secret)
let dbref = FIRDatabase.database().reference()
let usersref = dbref.child("users")
let uid = session?.userID
//let user = FIRAuth.auth?.signIn
print("Creating user")
let newUserReference = usersref.child(uid!)
newUserReference.setValue(["username": session?.userName])

Okay so after some debugging it was pretty simple where I went wrong. I was trying to write to the database before I'd authenticated with the database. Once I had put my code for writing to the database after I'd authenticated it all worked correctly.

Related

How to access Firebase Firestore data when login in using Firebase Auth?

On an application I'm building, I need to access the stored documents on Firestore after someone logs in using Auth. I have stored data on Firestore when someone creates an account: name, phone, email. I want to access this information when they log back in somewhere else. Is there a way to link the two on account creation or is there some other way to access the documents?
After looking around on the internet for a while, I haven't found any questions related to this, other than trying to login using Firestore, but it had no answers.
Account creation code:
Auth.auth().createUser(withEmail: email.text!, password: pass.text!) { (user, error) in
if user != nil {
self.performSegue(withIdentifier: "toFinishCreation", sender: self)
} else {
}
}
Storing data on Firestore:
FirebaseFirestore.root.collection("users").document(username).setData([
"name": username,
"phone": userphone,
"email": useremail], completion: { (err) in
if let err = err {
print(err.localizedDescription)
} else {
print("Document added!")
}
}
)
(Btw I used a struct on another swift file using vars called FirebaseFirestore and root)
Login code:
Auth.auth().signIn(withEmail: loginEmail.text!, password: loginPassword.text!) { (user, error) in
if user != nil {
self.performSegue(withIdentifier: "toHome", sender: self)
} else {
}
}
To summarize, how do I access the data that I stored on account login?
Can anyone help me with this? Anything is appreciated!
To fetch a document of the logged-in user you required document id which is unique. So, add user id as a document id. You will get user id in response while logged in and sign up and then, you can use that id to fetch logged in the user's document.
When creating a user, create a new Auth in FirebaseAuth as well as the Firestore. Once the user is logged in with the email address, find the user with that email. Although I have not used firestore before, note that this code is done with firebase database instead but the same should apply:
Auth.auth().createUser(withEmail: email, password: password, completion: { (user: User?, error) in
if error == nil {
print("You have successfully signed up")
guard let uid = user?.uid else {
return
}
//successfully authenticated user
let values = ["name": name, "email": email, "isShop" : shopAccount]
let ref = Database.database().reference()
let usersReference = ref.child("users").child(uid)
usersReference.updateChildValues(values, withCompletionBlock: { (err, ref) in
ref.child("Orders").child("Total Orders").setValue(0)
let vc = self.storyboard?.instantiateViewController(withIdentifier: "MainPageViewController")
self.present(vc!, animated: true, completion: nil)
})
This will then create a user in the FirebaseAuth, and add a new user to the Users section of the database under the uid that is assigned when the account is created. Then you can access this later when the user logs back in as you can retrieve the uid, and find the part of the database containing the information using this code:
dataRef.child("users").child(userID!).observeSingleEvent(of: .value, with: {(snapshot) in
As a result, this can be applied to firestore by adding a new document and title to each user added to your FirebaseAuth in order to retrieve future information

How to get username from AWS Cognito - Swift

Q & A Style: See Answer Below
How Can I get the username from a user logged in with Cognito?
I've done this and my user is logged in, now what?
AWSAuthUIViewController.presentViewController(
with: self.navigationController!,
configuration: config, completionHandler: { (provider: AWSSignInProvider, error: Error?) in
if error == nil {
//get parameters
}
} else {
print(error as Any)
}
})
}
Prerequisites:
App registered with MobileHub
Cognito Setup in MobileHub
Mobilehub integrated with Swift Project using AWS SDK
If you're like me, you did this with little to no difficulty and now you're stuck trying to get the username and other parameters from the logged in user. There are a lot of answers, but thus far, I haven't stumbled upon one that gets you all the way there.
I was able to piece this together from various sources:
func getUsername() {
//to check if user is logged in with Cognito... not sure if this is necessary
let identityManager = AWSIdentityManager.default()
let identityProvider = identityManager.credentialsProvider.identityProvider.identityProviderName
if identityProvider == "cognito-identity.amazonaws.com" {
print("************LOGGED IN WITH COGNITO************")
let serviceConfiguration = AWSServiceConfiguration(region: .USWest2, credentialsProvider: nil)
let userPoolConfiguration = AWSCognitoIdentityUserPoolConfiguration(clientId: "YourClientID", clientSecret: "YourSecretKey", poolId: "YourPoolID")
AWSCognitoIdentityUserPool.register(with: serviceConfiguration, userPoolConfiguration: userPoolConfiguration, forKey: "YourPoolName (typically formatted as YourAppName_userpoool_MOBILEHUB_12345678")
let pool = AWSCognitoIdentityUserPool(forKey: "YourPoolName")
// the following line doesn't seem to be necessary and isn't used so I've commented it out, but it is included in official documentation
// let credentialsProvider = AWSCognitoCredentialsProvider(regionType: .USWest2, identityPoolId: "YourPoolID", identityProviderManager:pool)
if let username = pool.currentUser()?.username {
print("Username Retrieved Successfully: \(username)")
} else {
print("Error getting username from current user - attempt to get user")
let user = pool.getUser()
let username = user.username
print("Username: \(username)")
}
}
}
To get your ClientID, Secret Key, and PoolID, check your awsconfiguration.json
To get your PoolName, login to MobileHub, and in your project's backend, go to User Sign in, click Email and Password, then click Edit in Cognito. The following page will have your Pool Name as "YourAppName_userpool_MOBILEHUB_12345678"
Edit: To get all of the attributes as well:
if let userFromPool = pool.currentUser() {
userFromPool.getDetails().continueOnSuccessWith(block: { (task) -> Any? in
DispatchQueue.main.async {
if let error = task.error as NSError? {
print("Error getting user attributes from Cognito: \(error)")
} else {
let response = task.result
if let userAttributes = response?.userAttributes {
print("user attributes found: \(userAttributes)")
for attribute in userAttributes {
if attribute.name == "email" {
if let email = attribute.value {
print("User Email: \(email)")
}
}
}
If you're using Cognito User Pools, you can use this:
import AWSUserPoolsSignIn
AWSCognitoUserPoolsSignInProvider.sharedInstance()
.getUserPool()
.currentUser()?
.username

Swift Firebase 4.0 - observeSingleEvent not returning data set (but authentication is working)

I have been working with Firebase on Android for the last 12 months+, with success. I just switched over to Swift and am attempting to read data from the same Firebase database I created and have been using the last 12 months+. Since there is security applied to the FB DB the first thing I did was to get FB authentication (from Swift) working. This works. Now, I am trying to get a simple observeSingleEvent operational and am struggling.
The Firebase DB is the same old stuff. It has a users node off of the root. For starters I would just like to read in a user from the user's node. I know authentication is working because when I submit my email and password I receive a confirmation. When the email / password are wrong I do not get confirmation. Given this validates my connection to the DB and as a test I stuck the following code in right after login validation.
When I debug this it simply skips from the "self.ref?.child("users").observeSingleEvent..." to the bracket below. i.e. It never acknowledges there is data available, but there is data available.
To avoid anyone asking "What do you need?" What I am looking for is an answer to why I receive no data result set with the code below given there is data in the FB DB and I have been reading/writing that data on Android for the last 12+ months.
Any/all help is welcome as I cut my teeth on Swift / FB 4.0
#IBAction func signInButtonTapped(_ sender: UIButton) {
// TODO: Do some form validation on the email and password
if let email = emailTextField.text, let pass = passwordTextField.text {
// Check if it's sign in or register
if isSignIn {
// Sign in the user with Firebase
Auth.auth().signIn(withEmail: email, password: pass, completion: { (user, error) in
// Check that user isn't nil
if let u = user {
// User is found, go to home screen
self.ref = Database.database().reference()
self.ref?.child("users").observeSingleEvent(of: .value, with: {( snapshot) in
let value = snapshot.value as? NSDictionary
let username = value?["username"] as? String ?? ""
print ("*** " + username)
})
}
else {
// Error: check error and show message
}
})
}
Since observeSingleEvent is an async call so it is running on the background thread, that is why it jumps on to the next bracket when you put a break point on self.ref, because it doesnt block the current thread of execution and does execute after sometime on background thread
One way to do is this:
#IBAction func signInButtonTapped(_ sender: UIButton) {
// TODO: Do some form validation on the email and password
if let email = emailTextField.text, let pass = passwordTextField.text {
// Check if it's sign in or register
if isSignIn {
// Sign in the user with Firebase
Auth.auth().signIn(withEmail: email, password: pass, completion: { (user, error) in
// Check that user isn't nil
if let u = user {
// User is found, go to home screen
self.fetchUserName { fetchedUserName in
print(fetchedUserName)
}
}
else {
// Error: check error and show message
}
})
}
func fetchUserName(completionHandler: #escaping (String) -> Void) {
self.ref = Database.database().reference()
self.ref?.child("users").observeSingleEvent(of: .value, with: {( snapshot) in
//let value = snapshot.value as? NSDictionary
if let value = snapshot.value as? [String: Any] {
print(value)
let username = value["username"] as? String ?? ""
print (username)
completionHandler(username)
}
})
}

How to check if user needs to re-authenticate using Firebase Authentication

I am using Firebase to log in users into my app, but when I am adding the capability to manage their account like changing their email, password and so on. The documentation says that if the user have not recently signed in they need to re-authenticate, but my question is: How can I check if the user have signed in recently or not? According to the docs the error will return FIRAuthErrorCodeCredentialTooOld, but how can I check this?
Swift 3
I had to do this yesterday when trying to delete a user. One thing to note is FIRAuthErrorCodeCredentialTooOld is now FIRAuthErrorCode.errorCodeRequiresRecentLogin
What I did was trigger a UIView to ask for log in details if that error is thrown. Since I was using email and password, that's what I collected from the user in my example.
private func deleteUser() {
//get the current user
guard let currentUser = FIRAuth.auth()?.currentUser else { return }
currentUser.delete { (error) in
if error == nil {
//currentUser is deleted
} else {
//this gets the error code
guard let errorCode = FIRAuthErrorCode(rawValue: error!._code) else { return }
if errorCode == FIRAuthErrorCode.errorCodeRequiresRecentLogin {
//create UIView to get user login information
let loginView = [yourLoginUIViewController]
self.present(loginView, animated: true, completion: nil)
}
}
}
Once I had the login information I ran this function to reauthenticate the user. In my case I ran it the loginView in the above code if the login in was successful:
func reauthenticateUserWith(email: String, password: String) {
FIRAuth.auth()?.signIn(withEmail: email, password: password) { (user, error) in
if error == nil {
//display UIView to delete user again
let deleteUserView = deleteUserView()
present(deleteUserView, animated: true, completion: nil)
} else {
//handle error
print(error!.localizedDescription)
}
}
}
The deleteUserView in my case calls deleteUser() on a button tap from the user. You can also use UIAlertController in place of the custom UIViews, but that's up to you.
Hope this helps.
Update for current Swift 5
let user = Auth.auth().currentUser
user?.delete { error in
if let error = error {
let authErr = AuthErrorCode(rawValue: error.code)
if authErr == .requiresRecentLogin {
// reauthenticate
}
// other error
} else {
// delete success
}
}
According to the documents, there is currently no way to check FIRAuthErrorCodeCredentialTooOld other than going through the deleting of the account or the other sensitive cases mentioned.
If you are like me and ended up here because you are trying to figure out how to handle removing someone from Auth and removing other user data in Cloud Firestore, Realtime Database, and/or Cloud Storage, then there is a better solution.
Check out the Delete User Data Extension from Firebase to handle this. In short, when a user profile is deleted from Auth, you can use this also to delete data associated with the uid from those other Firebase data storage tools.

declined Permission handling Facebook Login

I am trying to force my users to provide their data in order to get access to my app.
Since the syntax in Swift 3 changed a little, I am stuck in developing this. Basically my idea is the following:
let permissions = ["public_profile", "user_birthday", "user_photos"]
PFFacebookUtils.logInInBackground(withReadPermissions: permissions, block:
{ (user, error) -> Void in
// casting user to FBSDKLoginManagerLoginResult
// asking for specific data declined/granted Permissions
if let user = user as? FBSDKLoginManagerLoginResult {
print(user.declinedPermissions)
print(user.grantedPermissions)
}}
My print method will never be called. What is the real way to cast from user(PFUser?) to FBSDKLoginManagerLoginResult ?
Try getting back the actual FBSDKLoginManagerLoginResult as the result instead of user.
let login:FBSDKLoginManager = FBSDKLoginManager()
let permissions = ["public_profile", "user_birthday", "user_photos"]
login.logIn(withReadPermissions: permissions, from: self) { (result: FBSDKLoginManagerLoginResult?, error: Error?) -> Void in
if(FBSDKAccessToken.current() != nil){
let permissions = FBSDKAccessToken.current().permissions
print(permissions)
//Do whatever else you need to do with the result
}