How to read the current users profile document in Firestore - ios

I am working on my first IOS app using Firestore. I am successfully retrieving all user documents or a specific document with a document name from the "users" collection. But as soon as I want to query the currentUsers documents I have trouble. I need the data for a user profile viewController.
I can query a specific document and print it with this code:
func loadUserData() {
let userDocumentRef = Firestore.firestore().collection("users").document(
"6tIzsXgbOMDIFpdgR18i")
userDocumentRef.getDocument { (document, error) in
if let document = document, document.exists {
let userData = document.data().map(String.init(describing: )) ?? "nil"
print("Document data: \(userData)")
} else {
print("document does not exist")
}
}
}
But as soon as I try to get the currentUsers document with this code, then I get the error that "document does not exist":
func currentUserData() {
let userID : String = (Auth.auth().currentUser?.uid)!
let userRef = Firestore.firestore().collection("users").document(userID)
userRef.getDocument { (document, error) in
if let document = document, document.exists {
let userData = document.data().map(String.init(describing: )) ?? "nil"
print("Document data: \(userData)")
} else {
print("document does not exist")
}
}
}
Here is a screenshot of the users collection in Firestore.
Screenshot of Firestore users collection
I just simply cannot figure out what I am doing wrong, so any help is appreciated.

It seems like there is an issue when mapping the document. Here is how you can map data from Firestore to Swift:
struct UserProfile: Codable, Identifiable {
#DocumentID var id: String?
var userId: String
var firstName: String
// ... other attributes
}
func currentUserData() {
// Note: this is unsafe, please use an auth state change listener instead
// Also, please try to avoid using force unwrap - your app will crash if the attribute is nil
let userID : String = (Auth.auth().currentUser?.uid)!
let userRef = Firestore.firestore().collection("users").document(userID)
userRef.getDocument { (document, error) in
if let document = document, document.exists {
let userData = document.data(as: UserProfile.self)
print("Document data: \(userData)")
} else {
print("document does not exist")
}
}
}
Here is how to set up an auth state change listener: https://firebase.google.com/docs/auth/ios/start#listen_for_authentication_state

Related

How do I Display First Name of User Once They have logged in?

I am using this code to retrieve the first name from my Firebase database and trying to display it in a label but it always returns the "Document does not exist in cache". My firebase security is set to read and write true. Do you know what I am doing wrong?
func nameGreeting() -> String{
let db = Firestore.firestore()
let userId = Auth.auth().currentUser!.uid;(Auth.auth().currentUser!.uid);
let docRef = db.collection("users").document(userId)
docRef.getDocument(source: .cache) { (document, error) in
if let document = document {
let name = document.get("firstname")
print("Cached document data: \(String(describing: name))")
} else {
print("Document does not exist in cache")
}
}
return ""
}
you are using an async function (docRef.getDocument...) inside "nameGreeting", so you are returning "" before the firebase function is finished. Try something like this:
func nameGreeting(completion: #escaping ((String?) -> Void)) {
let db = Firestore.firestore()
let userId = Auth.auth().currentUser!.uid
let docRef = db.collection("users").document(userId)
docRef.getDocument(source: .cache) { (document, error) in
if let document = document {
let name = document.get("firstname")
print("Cached document data: \(String(describing: name))")
completion(name)
} else {
print("Document does not exist in cache")
completion(nil)
}
}
}
and call it like this:
nameGreeting() { userName in
print("----> userName: \(userName)")
}

Cloud Firestore unable to retrieve document or field

I've used the sample code provided by the Firebase Documentation and it prints out the error message. I have no clue if the issue is within the code or within the structure of the database, as there are also sub-collections. In this case, I am trying to retrieve the "Home Title" field, however I heard that that's not possible (I may be wrong), so I'm trying to retrieve the "Sample Wedding" document, to no avail. This is my very first time programming a project in Swift and also using Firestore.
Here's my code:
let db = Firestore.firestore()
let docRef = db.collection("Weddings").document("Sample Wedding")
docRef.getDocument { (document, error) in
if let document = document, document.exists {
let dataDescription = document.data().map(String.init(describing:)) ?? "nil"
print("Document data: \(dataDescription)")
} else {
print("Document does not exist")
}
Here's my database structure:
]
You can try it like this
let db = Firestore.firestore()
db.collection("Weddings").document("Sample Wedding").getDocument {
(documentSnapshot, err) in
if let err = err {
print("Error getting documents: \(err)")
} else {
print("Document data: \(documentSnapshot)")
if let title = documentSnapshot.get("Home Title") as? String {
print(title)
}
}
}

SwiftUI - assign Firbase Database output to Data Model

im new to swift and Firebase. And im trying to understand it.
I have a Cloud Firestore DB, where i store some user data like username and email. Now i want the output of the given User assign to my UserData Model for easy use. i really dont know how to achiev this.
These are my two Files:
User.swift
import SwiftUI
import Firebase
import FirebaseFirestore
struct User {
var username : String
var email : String
}
GetData.swift
let docRef = db.collection("users").document(Auth.auth().currentUser?.uid)
docRef.getDocument { (document, error) in
if let document = document, document.exists {
let dataDescription = document.data().map(String.init(describing:)) ?? "nil"
print("Document data: \(dataDescription)")
} else {
print("Document does not exist")
}
}
The Variable dataDescription is givin me a dictonaryArray, with all the needed values. And now i need to assign that Dictonary to my User.swift struct.
best regards!
fYI: Firebase Printscreen
I'd recommend using Codable where you can. I'm using the CodableFirebase Cocoapod to help with Firebase parsing. Below is an example how to use this:
let docRef = db.collection("users").document(Auth.auth().currentUser?.uid)
docRef.getDocument { (document, error) in
guard let value = document.value, value as? NSNull == nil else {
return
}
do {
let newValue = try self.decoder.decode(User.self, from: value)
///New value created here
} catch let error {
print(error)
}
}
You can make this more generic by passing through a class type and having an optional instance returned:
static func item<T: Codable>(_ item: T.Type, docRef: DatabaseReference, completion: #escaping ((Result<T?, Error>)->Void)) {
docRef.getDocument { (document, error) in
guard let value = snapshot.value, value as? NSNull == nil else {
completion(.success(nil))
return
}
do {
let newValue = try self.decoder.decode(T.self, from: value)
completion(.success(newValue))
} catch let error {
completion(.failure(error))
}
}
}
try
GetData.swift
var user = User()
let docRef = db.collection("users").document(Auth.auth().currentUser?.uid)
docRef.getDocument { (document, error) in
if let document = document, document.exists {
let dataDescription = document.data().map(String.init(describing:)) ?? "nil"
print("Document data: \(dataDescription)")
user.username = document.data(["username"]) as! String
user.email= document.data(["email"]) as! String
} else {
print("Document does not exist")
}
}

Storing asynchronous Cloud Firestore query results in Swift

I am working on a simple project using Swift 5, SwiftUI and Firebase which cycles through given ids in an array, searching in the Cloud Firestore database for each id, and appending the corresponding name associated with the id to a new array.
Here is a picture of my database:
For example, I am given an array a few ids, then for each element in the given array, I get the document associated with that id, then print the "firstname" field in that document.
However, I want to store each "firstname" value retrieved into a separate array locally for use later. In Javascript, I know this is done using await and async functions, but I found out through countless hours of troubleshooting that Swift doesn't have async or await.
Here is my code:
func convertToNames(arr: [String]) -> [String]{
var newArr : [String] = []
for id in arr {
let docRef = db.collection("users").document(id)
docRef.getDocument { (document, error) in
if let document = document, document.exists {
let dataDescription = document.data().map(String.init(describing:)) ?? "nil"
let data = document.get("firstname") ?? "nil"
print("gotten data: \(data)")
newArr.append(String(describing: data))
} else {
print("Document does not exist")
}
}
}
print("NEW ARRAY: \(newArr)")
return (newArr)
}
This code prints an empty array when finished, and I understand why but I just have no clue how to make it work in Swift. I've spent about 5 hours today sifting through the Firebase documentation, looking at example code, and sifting through Youtube, but none of the resources address this issue to the extent that I need. If it is impossible to do, please let me know.
You need a dispatch group in addition to a completion
func convertToNames(arr: [String],completion:#escaping(([String]) -> ())) {
var newArr : [String] = []
let g = DispatchGroup()
for id in arr {
let docRef = db.collection("users").document(id)
g.enter()
docRef.getDocument { (document, error) in
if let document = document, document.exists {
let dataDescription = document.data().map(String.init(describing:)) ?? "nil"
let data = document.get("firstname") ?? "nil"
print("gotten data: \(data)")
newArr.append(String(describing: data))
g.leave()
} else {
print("Document does not exist")
g.leave()
}
}
}
g.notify(queue:.main) {
print("NEW ARRAY: \(newArr)")
completion(newArr)
}
}
Call
convertToNames(arr:<#arr#>) { res in
print(res)
}

How to get post count of user using firestore in iOS swift

I have migrated user post, followers and following from from firebase to firestore. Now i have migrated post, followers and following and post, followers count too.
Here the code i have migrated from firebase to firestore.
import Foundation
import FirebaseDatabase
import FirebaseFirestore
class FollowApi {
var REF_FOLLOWERS = Database.database().reference().child("followers")
var REF_FOLLOWING = Database.database().reference().child("following")
let db = Firestore.firestore()
func followAction(withUser id: String) {
let docRef = db.collection("user-posts").document(id)
docRef.getDocument { (document, error) in
if let document = document, document.exists {
let dataDescription = document.data().map(String.init(describing:)) ?? "nil"
print("Document data: \(dataDescription)")
self.db.collection("feed").document(API.User.CURRENT_USER!.uid).setData([document.documentID: true])
} else {
print("Document does not exist")
}
}
self.db.collection("followers").document(id).setData([API.User.CURRENT_USER!.uid: true])
self.db.collection("following").document(API.User.CURRENT_USER!.uid).updateData([id: true])
}
func unFollowAction(withUser id: String) {
let docRef = db.collection("user-posts").document(id)
docRef.getDocument { (document, error) in
if let document = document, document.exists {
let dataDescription = document.data().map(String.init(describing:)) ?? "nil"
print("Document data: \(dataDescription)")
self.db.collection("feed").document(API.User.CURRENT_USER!.uid).delete()
} else {
print("Document does not exist")
}
}
self.db.collection("followers").document(id).setData([API.User.CURRENT_USER!.uid: NSNull()])
self.db.collection("following").document(API.User.CURRENT_USER!.uid).setData([id: NSNull()])
}
func isFollowing(userId: String, completed: #escaping (Bool) -> Void) {
let docRef = db.collection("followers").document(userId)
docRef.getDocument { (document, error) in
if let document = document, document.exists {
print("documnetData::::\(String(describing: document.data()))")
if let dataDescription = document.data(), let _ = dataDescription[API.User.CURRENT_USER!.uid] as? Bool {
completed(true)
}
completed(false)
} else {
completed(false)
}
}
}
func fetchCountFollowing(userId: String, completion: #escaping (Int) -> Void) {
// REF_FOLLOWING.child(userId).observe(.value, with: {
// snapshot in
// let count = Int(snapshot.childrenCount)
// completion(count)
// })
db.collection("following").document(userId).getDocument { (querySnapshot, error) in
let count = Int((querySnapshot?.documentID)!)
print("followingCount::::\(String(describing: count))")
completion(count!)
}
}
}//followAPI
I tried to get following counts from firestore.
let count = Int((querySnapshot?.documentID)!)
print("followingCount::::\(String(describing: count))")
completion(count!)
but does not show any any yet all. I do not know what mistake i have done ?
Any help much appreciated pls....
If you're querying for a collection then its snapshot will contain an array of documents. What are you trying to get is a documentID which is same as key in Firebase.
Firestore | Firebase
documentID = snapshot.key
documentData = snapshot.value
Now, Come to the main point and here is what you need to get the count.
let count = querySnapshot?.documents.count
print(count)
EDIT For Comment: how can i migrate REF_FOLLOWING.child(userId).observe(.value, with: { snapshot in let count = Int(snapshot.childrenCount) completion(count) }) to firestore
Based on attached DB structure you're fetching following corresponding to userId which is a Document.
REF_FOLLOWING.document(userId).getDocument { (snapshot, error) in
if let _error = error {
print(_error.localizedDescription)
return
}
guard let _snapshot = snapshot else {return}
/// This is a single document and it will give you "[String:Any]" dictionary object. So simply getting its count is the result you needed.
let dict = _snapshot.data()
print(dict.count)
}

Resources