firestore fetch subcollection - swift

I'm trying to fetch subcollection of my users document with code below
func readData(){
let userId = Auth.auth().currentUser?.uid
self.db.collection("users/\(userId)/saved").getDocuments { (snapshot, err) in
if let err = err {
print("err")
}
if let userId != nil {
for document in snapshot!.documents {
let docId = document.documentID
let cty = document.get("city") as! String
let ccode = document.get("code") as! String
let countr = document.get("country") as! String
print(cty, ccode, countr,docId)
}
}
}
but my code doesn't print anything, I don't understand the problem, documents exsist, see picture below

You're using illegal syntax with the userId check in the snapshot return but the logic flow is the bigger problem. I would recommend you check if the user is signed in before grabbing the subcollection and checking if there is a viable snapshot instead of checking the state of authentication.
func readData() {
guard let userId = Auth.auth().currentUser?.uid else {
return
}
db.collection("users/\(userId)/saved").getDocuments { (snapshot, error) in
guard let snapshot = snapshot else {
if let error = error {
print(error)
}
return
}
for doc in snapshot.documents {
guard let city = doc.get("city") as? String,
let code = doc.get("code") as? String,
let country = doc.get("country") as? String else {
continue // continue document loop
}
let docId = doc.documentID
print(city, code, country, docId)
}
}
}

Related

How can i retrieve data from a collection if the condition is set firestore swift

Here is my code pls help
My target is to retrieve a list of channels that contain uid within Subscriptions collection.
func getUserSubscriptions(uid: String) {
db.collection("Channels").addSnapshotListener { (querySnapshot, error) in
guard let documents = querySnapshot?.documents else {
//No documents found
return
}
for document in querySnapshot!.documents {
document.reference.collection("Subscribers").whereField("user_id", isEqualTo: uid).addSnapshotListener { (querySnapshot, error) in
guard (querySnapshot?.documents) != nil else {
//No documents found
return
}
//I want to get Channels that only contain uid withing it's Subscribers collection, but unfortunately it only gets the whole Channels, please help.
DispatchQueue.main.async {
self.channels = documents.map { d in
return Channels(id: d.documentID, channel_id: d["channel_id"] as! String?, channel_name: d["channel_name"] as? String ?? "", creator: d["creator"] as? String ?? "")
}
}
}
}
}
}
Pls help I got stuck here.

How to get the first name of the logged in User from firebase using Swiftui?

How to to get the first name of the current user which is logged in.
This is how my try looks like:
var ref: DatabaseReference!
ref = Database.database().reference()
let db = Firestore.firestore()
let userID = Auth.auth().currentUser?.uid
print(userID)
ref.child("users").child(userID!).observeSingleEvent(of: .value, with: { (snapshot) in
// Get user value
let value = snapshot.value as? NSDictionary
let username = value?["firstname"] as? String ?? ""
print(username)
// ...
}) { (error) in
print(error.localizedDescription)
}
Try the following:
let userId = Auth.auth().currentUser?.uid else { return }
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: \(name)")
} else {
print("Document does not exist in cache")
}
}
You are using cloud firestore but in your code, you are using the Realtime database. You need to check the following docs related to cloud firestore:
https://firebase.google.com/docs/firestore/quickstart

Would like to use DispatchQueue.global().async and main.async, but it doesn't work well [duplicate]

This question already has answers here:
Wait until swift for loop with asynchronous network requests finishes executing
(10 answers)
Closed 4 years ago.
I would like to use asynchronous tasking for my app with using DispatchQueue.global().async and DispatchQueue.main.async, but it doesn't work.
I would like to get the data from firebase and then make List and pass it to closure. But in the code below, the timing completion called is first and then posts.append is called.
func retrieveData(completion: #escaping ([Post]) -> Void) {
var posts: [Post] = []
let postsColRef = db.collection("posts").order(by: "createdAt").limit(to: 3)
postsColRef.getDocuments() { (querySnapshot, error) in
if let error = error {
print("Document data: \(error)")
} else {
DispatchQueue.global().async {
for document in querySnapshot!.documents {
let data = document.data()
let userId = data["userId"] as? String
let postImage = data["postImageURL"] as? String
let createdAt = data["createdAt"] as? String
let docRef = db.collection("users").document(userId!)
docRef.getDocument() { (document, error) in
if let document = document, document.exists {
let data = document.data()!
let userName = data["userName"] as? String
let post = Post(
userId: userId!,
userName: userName!,
postImageURL: postImage!,
createdAt: createdAt!
)
print("When append called")
posts.append(post)
}
}
}
DispatchQueue.main.async {
print("When completion called")
print(posts)
completion(posts)
}
}
}
}
}
I would like to complete for loop at first, and then go to completion. Could anybody give me any idea?
I just found this question(Wait until swift for loop with asynchronous network requests finishes executing) and tried the code below and it worked. I'm sorry for everybody who checked this question. From next time, at first I'm going to search the existing questions. Thank you.
func retrieveData(completion: #escaping ([Post]) -> Void) {
var posts: [Post] = []
let postsColRef = db.collection("posts").order(by: "createdAt").limit(to: 3)
let group = DispatchGroup()
postsColRef.getDocuments() { (querySnapshot, error) in
if let error = error {
print("Document data: \(error)")
} else {
for document in querySnapshot!.documents {
group.enter()
let data = document.data()
let userId = data["userId"] as? String
let postImage = data["postImageURL"] as? String
let createdAt = data["createdAt"] as? String
//投稿に紐づくユーザーデータを取得して合わせてpostArrayに挿入
let docRef = db.collection("users").document(userId!)
docRef.getDocument() { (document, error) in
if let document = document, document.exists {
let data = document.data()!
let userName = data["userName"] as? String
let post = Post(
userId: userId!,
userName: userName!,
postImageURL: postImage!,
createdAt: createdAt!
)
posts.append(post)
group.leave()
}
}
}
group.notify(queue: .main) {
print(posts)
completion(posts)
}
}
}
}

Get single elements from Firestore Document

How can I get single items from my Document in Firebase?
I can get all elements with this code:
let key = UserDefaults.standard.value(forKey: "uid") as! String
let docRef = firebaseDB.collection("user").document(key)
docRef.getDocument { (document, error) in
if let document = document, document.exists {
let dataDescription = document.data().map(String.init(describing:))
print("Document data: \(dataDescription)")
} else {
print("Document does not exist")
}
}
I only wish to get the name and phone number. How can I filter my result?
FirebaseFirestore has a handy way of caching single documents:
First, you need to specify the document by it's ID:
let key = UserDefaults.standard.value(forKey: "uid") as? String ?? "Null" // Unique user key
let docRef = db.collection("user").document(key)
Get the document:
docRef.getDocument { (document, error) in
if let document = document, document.exists {
let docData = document.data()
// Do something with doc data
} else {
print("Document does not exist")
}
}
Access the doc data:
let status = docData!["phone"] as? String ?? ""

Firebase - Swift - Delete a child UID from a snapshot

I am trying to delete up the studentUser UID from my staffUsers. The UID I want to delete is nested in the staffUsers -> studentSession1List.
I have the UID listed with A Bool of True on creation. This "studentSession1List" will have lots of studentUsers in the list. I only want the studentUser that is logged in to have their UID(55FDLm9n6LccZBB7skaCbvfSHRz1) removed from the list.
let dataref = Database.database().reference()
dataref.child("staffUsers").queryOrdered(byChild: "studentSession1List").observe(.value, with: { (snapshot) in
for snap in snapshot.children {
guard let studentUID = Auth.auth().currentUser?.uid else { return }
let snapDataSnapshot = snap as! DataSnapshot
var snapValues = snapDataSnapshot.value as? [String: AnyObject]
if var snapWithReg = snapValues?["studentSession1List"] as? [String: Bool] {
print("This is the staff member")
print(snapWithReg)
print(snapWithReg.count)
snapWithReg.removeValue(forKey: studentUID)
}
}
}) { (error) in
print(error.localizedDescription)
}
Here is the output:
Full Function for Deleting and Adding the Student
func didSelect(for cell: StudentSearchCell) {
guard let indexpath = collectionView?.indexPath(for: cell) else { return }
let staffUser = self.users[indexpath.item]
let selectedUserId = staffUser.uid
guard let studentUID = Auth.auth().currentUser?.uid else { return }
let dataRef = Database.database().reference()
dataRef.child("staffUsers").queryOrdered(byChild: "studentSession1List").observe(.value, with: { (snapshot) in
for snap in snapshot.children {
guard let studentUID = Auth.auth().currentUser?.uid else { return }
let snapDataSnapshot = snap as! DataSnapshot
var snapValues = snapDataSnapshot.value as? [String: AnyObject]
if (snapValues? ["studentSession1List"] as? [String: Bool]) != nil {
dataRef.child("staffUsers").child(snapDataSnapshot.key).child("studentSession1List").child(studentUID).removeValue(completionBlock: { (error, ref) in
if error != nil {
print("Error: \(String(describing: error))")
return
}
print("Removed successfully")
})
}
}
}) { (error) in
print(error.localizedDescription)
}
// Add student to staff list
let ref = Database.database().reference().child("staffUsers").child(selectedUserId).child("studentSession1List")
let values = [studentUID: true]
ref.updateChildValues(values) { (err, ref) in
if let err = err {
print("Failed to follow user:", err)
return
}
}
// Add selected staff to student list
let studentRef = Database.database().reference().child("studentUsers").child(studentUID).child("studentSession1List")
studentRef.removeValue()
let studentValues = [selectedUserId: true]
studentRef.updateChildValues(studentValues) { (err, studentRef) in
if let err = err {
print("Failed to follow user:", err)
return
}
}
self.navigationController?.popViewController(animated: true)
}
I think you need to reach the child that you want to remove using the following code and then remove it.
Edit1:
Since inside staffUsers we have keys inside which studentSession1List is present inside which the value (studentUID) is present that we want to remove, so inside your already written code I have added the new code, please check
let dataref = Database.database().reference()
dataref.child("staffUsers").queryOrdered(byChild: "studentSession1List").observe(.value, with: { (snapshot) in
for snap in snapshot.children {
guard let studentUID = Auth.auth().currentUser?.uid else { return }
let snapDataSnapshot = snap as! DataSnapshot
var snapValues = snapDataSnapshot.value as? [String: AnyObject]
if var snapWithReg = snapValues?["studentSession1List"] as? [String: Bool] {
//Added code here
dataref.child("staffUsers").child(snapDataSnapshot.key).child("studentSession1List").child(studentUID).removeValue(completionBlock: { (error, ref) in
if error != nil {
print("Error: \(error)")
return
}
print("Removed successfully")
})
}
}
}) { (error) in
print(error.localizedDescription)
}
Edit2:
To delete the code once , we can use observeSingleEvent
observeSingleEvent(of: .value, with: { (snapshot) in
}, withCancel: nil)