Retrieving value from firebase in iOS - ios

I want to retrieve the database value on firebase in an iOS application. I've already installed firebase on my iOS project but I've tried multiple times but it shows me errors. Someone can help me?

This is an example f collecting something from the database:
let userID = FIRAuth.auth()?.currentUser?.uid
FIRDatabase.database().reference().child("users").child(userID!).observeSingleEventOfType(.Value, withBlock: { error, snapshot in
if error != nil {
print(error)
} else {
// Get user value
let test_retrieve = snapshot.value!["name"] as! String
let user = User.init(username: username)
}
})

Related

Firestore search for String in Array in Document

I want to search for an specific string value in an document which is in an array.
Here is my database:
This is my code so far: But it returns 0 documents:
func changePhotoUrlInPosts(url: String) {
let db = Firestore.firestore()
let user = UserService.currentUserProfile!
db.collection("posts")
.whereField("username", isEqualTo: user.username)
.getDocuments { (snapshot, error) in
if let indeedError = error {
print(indeedError.localizedDescription)
return
}
guard let indeedSnapshot = snapshot else {
print("snapshot is empty")
return
}
for document in indeedSnapshot.documents {
document.setValue(url, forKey: "photoUrl")
}
}
}
How can I go into my array in this document?
Thanks
Your screenshot is showing data in Realtime Database, but your code is querying Firestore. They are completely different databases with different APIs. You can't use the Firestore SDK to query Realtime Database. If you want to work with Realtime Database, use the documentation here.
There is author between posts and username field in your data structure.
Your code means that right under some specific post there is username field.
So such code will work because date right undes post:
db.collection("posts").whereField("date", isEqualTo: "some-bla-bla-date")
In your case you have two options as I see:
duplicate username and place this field on the same level as
date and guests.
re-write you code to check username inside author document.
Hope it will help you in your investigation.
So I changed my code to:
func loadData(url: URL){
let ref = Database.database().reference().child("posts")
let user = UserService.currentUserProfile!
ref.queryOrdered(byChild: "author/username").queryEqual(toValue: user.username).observe(.value, with: { snapshot in
if var post = snapshot.value as? [String : Any] {
print("updated all Posts")
post.updateValue(url.absoluteString, forKey: "photoUrl")
print(post.values)
}else{
print("fail")
}
})
}
It went through and I get the print statement of my values but the data didn't changed in the realtime database

Firebase Sign in with apple. New user and returning user

I have implemented sign in with apple. I initiate the first time user this way:
let credential = OAuthProvider.credential(withProviderID: "apple.com",
idToken: idTokenString,
rawNonce: nonce)
then sign in the user:
// HERE: Auth.auth().currentUser is always nil for first time users AND returning users
Auth.auth().signIn(with: credential) { (authResult, error) in
// HERE: Auth.auth().currentUser is never nil for first time users AND returning users
if error == nil {
guard let uid = authResult?.user.uid else { return }
let databaseRef = Database.database().reference().child("Users/\(uid)")
//update realtime database here
}
}
It works fine for the first time user. The issue is when it is returning user.Basically I need two different actions for the first time user and returning users. The difference is for returning users, I do not add anything to the database
P.S. I followed the official document from firebase in here
It seems the following works
guard let uid = authResult?.user.uid else { return }
let db = Database.database().reference()
db.child("Users").observeSingleEvent(of: .value) { (DataSnapshot) in
if DataSnapshot.hasChild(uid){
//Existing user. No need to add anything to database
}else{
//New user. Add/Update database with some default values
}
I don't know if this is the best approach or not but works for me for now

How do I check if a Firestore document exists before I update it?

I'm newish to Swift and new to Firestore and am running into an issue that I can't solve. I have the code below which is supposed to check for a document at the UserReference location in Firestore. If it doesn't exist, it should create a new document that contains the pertinent user information that I have previously grabbed from facebook.
This what UserReference looks like self.UserReference = self.db.collection("users").document(UserID as! String) where the UserID is from the Facebook graph request. Next, it'll run a transaction to pull the document, update the user doc with the latest facebook info (assuming this is not a new user), and then update the doc to Firebase Firestore.
let db = Firestore.firestore()
var fbUserUpdate = User(
firstName: String(),
lastName: String(),
email: String(),
<extra stuff>)
func updateFacebookInfoToFirestore(completion: #escaping (_: User?, _: Error?) -> Void) {
// Check if user exists, if not create new user
UserReference!.getDocument { (document, error) in
if document != nil {
// continue
} else {
self.UserReference.setData(self.fbUserUpdate.dictionary)
}
}
// Now pull data and update based on most recent facebook info.
db.runTransaction({ (transaction, errorPointer) -> Any? in
// Read data from Firestore inside the transaction, so we don't accidentally
// update using staled client data. Error if we're unable to read here.
let UserSnapshot: DocumentSnapshot
do {
try UserSnapshot = transaction.getDocument(self.UserReference!)
} catch let error as NSError {
errorPointer?.pointee = error
return nil
}
// This will overwrite the fbUserUpdate Struct with the updated information.
guard var dbUser = User(dictionary: UserSnapshot.data()) else {
let error = NSError(domain: "Domain", code: 0, userInfo: [
NSLocalizedDescriptionKey: "Unable to write to userdata at Firestore path: \(self.UserReference!.path)"
])
errorPointer?.pointee = error
return nil
}
// Update from Facebook
dbUser.firstName = self.fbUserUpdate.firstName
dbUser.lastName = self.fbUserUpdate.lastName
dbUser.email = self.fbUserUpdate.email
// Load new data to firestore
transaction.setData(dbUser.dictionary, forDocument: self.UserReference!, options: SetOptions.merge())
return nil
}) { (object, error) in
if let error = error {
print(error)
} else {
// nothing yet
}
}
}
However, when I run this in my app, when I get to the UserReference!.getDocument closure, it skips over the closure and then the transaction doesn't work as intended because the try UserSnapshot = transaction.getDocument(self.UserReference!) returns a null since no document exists.
I believe the issue is in the .getDocument closure, but I don't know where I'm going wrong. I've tried to emulate the FriendlyEats firestore example code as best I can but I'm stuck and am in need of an extra set of eyes.

Unable to persist 'posts' reference for 'users' in Firebase

I'm creating a social app and a common aspect of it is keeping tabs of the posts that users make. I'm using Firebase as the backend for this and here's my schema
SocialApp
posts
-KGsBG5TPYBtzRivZnbf
users
facebook:10154108240254134
cachedProfile
displayName: "Karthik Kannan"
imageURL: "https://scontent.xx.fbcdn.net/v/t1.0-1/p100x100..."
posts
-KGsBG5TPYBtzRivZnbf: true
provider: "facebook"
When i go ahead and add posts from one device(or simulator) this works as planned but when i delete the app from a device and reinstall it, the posts relationship in the users dictionary disappears. I would like it to persist across devices and keep the relationship intact so if a user decides to use another iDevice to login his posts don't get orphaned.
Here's the code I wrote for adding a post to Firebase and how I currently keep track of the user.
func postToFirebase(imgUrl: String) {
var gift: Dictionary<String, AnyObject> = [
"giftName": giftName.text!,
"giftDescription":giftDescription.text!,
"giftAvailableAt": giftAvailableAt.text!,
"giftPrice": Int(giftPrice.text!)!,
"username": NSUserDefaults.standardUserDefaults().valueForKey("uid")!,
"giftImage":imgUrl,
]
let firebasePost = DataService.ds.REF_POSTS.childByAutoId()
firebasePost.setValue(gift)
firebasePost.observeSingleEventOfType(.Value, withBlock: { snapshot in
if let postID = snapshot.key {
DataService.ds.REF_USER_CURRENT.childByAppendingPath("posts").childByAppendingPath(postID).setValue(true)
}
})
}
This is how I set current users:
var REF_USER_CURRENT:Firebase {
if let uid = NSUserDefaults.standardUserDefaults().valueForKey("uid") as? String {
let user = Firebase(url:"\(BASE_URL)").childByAppendingPath("users").childByAppendingPath(did){
return user!
} else {
return Firebase()
}
}
func createFirebaseUser(uid:String, user:Dictionary<String, AnyObject>) {
REF_USERS.childByAppendingPath(uid).setValue(user)
}
And this is the code in my login controller.
NSUserDefaults.standardUserDefaults().setValue(authData.uid, forKey: "uid")
let user = ["provider":authData.provider!,"displayName":authData.providerData["displayName"]!,"cachedProfile":authData.providerData["cachedUserProfile"]!, "imageURL":authData.providerData["profileImageURL"]!]
DataService.ds.createFirebaseUser(authData.uid, user: user)
self.performSegueWithIdentifier("loggedIn", sender: nil)
This is all the code I've written. I suspect it's something to do with NSUserDefaults. Any help would be greatly appreciated by this beginner.
Check your code. I found a typo here:
var REF_USER_CURRENT:Firebase {
if let uid = NSUserDefaults.standardUserDefaults().valueForKey("uid") as? String {
let user = Firebase(url:"\(BASE_URL)").childByAppendingPath("users").childByAppendingPath(did){
return user!
} else {
return Firebase()
}
}
Instead of "...childByAppendingPath(did)"
You might want to change "did" to "uid"
Other than that, I have not tested out the rest of your code.
Seems like You have watched the Mark Price course, for making this app.
So have I :)

"cannot convert value of type" - error in Firebase Swift

I am trying to create a user in Firebase with an email & password and upon successful creation of the user record, I would go and update tree with additional attributes as child nodes of the user's UID. Here is what I have in code:
#IBAction func registerPressed(sender: AnyObject) {
let connection = Firebase(url:"https://something.firebaseio.com")
connection.createUser(self.emailTxtField.text, password: self.passwordTxtField.text,
withValueCompletionBlock: { error, result in
if error != nil {
// There was an error creating the account
print("Error Creating Account...Please try again")
} else {
let uid = result["uid"] as? String
print("Successfully created user account with uid: \(uid)")
/*After creating the user, we want to update the tree with other attributes*/
let userAdditionalDetails = ["name": self.nameTxtField.text, "mobile": self.mobileTxtField.text, "username": self.usernameTxtField.text]
let usersRef = connection.childByAppendingPath("users")
let users = ["\(uid)": userAdditionalDetails]
usersRef.setValue(users)
}
})
}
Although am not sure if the above will work or not (since this my first work with Firebase SDK), I am getting the following error:
cannot convert value of type “[String: [String : String?]]” to
expected argument type "AnyObject!"
I am unable of course to build the project due to the above error being triggered at line:
usersRef.setValue(users)
Any idea why this is happening and how to solve it?
Thanks in advance.
Found the solution and just having it here for someone else's future reference:
#IBAction func registerPressed(sender: AnyObject) {
let connection = Firebase(url:"https://something.firebaseio.com")
connection.createUser(self.emailTxtField.text, password: self.passwordTxtField.text,
withValueCompletionBlock: { error, result in
if error != nil {
// There was an error creating the account
print("Error Creating Account...Please try again")
} else {
let uid = result["uid"] as? String
print("Successfully created user account with uid: \(uid)")
/*After creating the user, we want to update the tree with other attributes*/
let userAdditionalDetails = ["name": "\(self.nameTxtField.text!)", "mobile": "\(self.mobileTxtField.text!)", "username": "\(self.usernameTxtField.text!)"]
let usersRef = connection.childByAppendingPath("users")
let users = ["\(uid)": userAdditionalDetails]
usersRef.setValue(users)
}
})
}
The catch is that when initializing userAdditionalDetails, should include the outlets variables in string format. E.g.
"username": "\(username.text!)"
This worked and all objects reflected correctly in backend.
In the call to usersRef.setValue(users), the error message states that the parameter is expected to be of type AnyObject!. Therefore, you must cast users to be of type AnyObject!:
let users = ["\(uid)": userAdditionalDetails]
usersRef.setValue(users as? AnyObject)

Resources