Cannot get documents from Firestore in Swift - ios

So I'm trying to get all documents from a specific collection from Firestore, but when stepping through the code, it skipped over the line and doesn't throw an error. I've used the exact same code in other parts of my app with success but it is not working here for some reason? Any help would be appreciated.
func getClientEmail() -> String? {
var clientEmail: String?
self.db.collection("Clients").getDocuments() { (querySnapshot, err) in
if let err = err {
print("error getting docs: \(err)")
} else {
for document in querySnapshot!.documents {
let result = Result {
try document.data(as: User.self)
}
switch result {
case .success(let client):
if let client = client {
if client.placementIDs[0] == self.user?.placementIDs[0] {
clientEmail = client.email
}
} else {
print("Document doesn't exist")
}
case .failure(let error):
print("Error decoding user: \(error)")
}
}
}
}
return clientEmail
}
After some testing, I moved this code to within the viewDidLoad() function and it worked... So I think it has something to do within it being wrapped in a function but not sure why, hope this information is helpful to anyone that might be able to fix this problem.

Thanks to Jay's comment, I managed to fix the problem by having clientEmail as a global variable and using a completion handler to assign the value in this function.
func getClientEmail() {
// Get email of client for corresponding contractor
db.collection("Clients").getDocuments(completion: { (querySnapshot, err) in
if let err = err {
print(err.localizedDescription)
return
} else {
for document in querySnapshot!.documents {
let result = Result {
try document.data(as: User.self)
}
switch result {
case .success(let client):
if let client = client {
if client.placementIDs[0] == self.user?.placementIDs[0] {
self.clientEmail = client.email
}
} else {
print("Document doesn't exist")
}
case .failure(let error):
print("Error decoding user: \(error)")
}
}
}
})
}

Related

Swift snapshot and mapping a single document from Firestore

I'm trying with no success on finding a way to retrieve only a single document instead of an array of documents from Firestore below is the code that I'm using for fetching ad array. Someone has suggestion on how to change fro getting only a document?
#Published var plantData: [PlantDataModel] = [] -> here I don't want an array
func loadData() {
print("FIREBASE LOADING DETAIL DATA VIEW")
db.collection("plantsData").whereField("plantId", isEqualTo: plant.idPlant).addSnapshotListener { querySnapshot, error in
if let querySnapshot = querySnapshot {
self.plantData = querySnapshot.documents.compactMap { document in
do {
let x = try document.data(as: PlantDataModel.self)
return x
} catch let error {
print("Errore fetching data: \(error)")
}
return nil
}
}
}
}
thank you
Replace
self.plantData = querySnapshot.documents.compactMap { document in
do {
let x = try document.data(as: PlantDataModel.self)
return x
} catch let error {
print("Errore fetching data: \(error)")
}
return nil
}
With
if let first = querySnapshot.documents.first {
do {
let x = try first.data(as: PlantDataModel.self)
self.plantData.append(x)
} catch let error {
print("Errore fetching data: \(error)")
}
}

How can I delete a Firestore field, using whereField?

I'm trying to delete a field within a document, when the field "uid" matches the Current User's ID. I'm pretty stuck on this, and would appreciate any help. I detail, below, my code and how my database is set up.
#IBAction func deleteAccountButtonIsTapped(_ sender: Any) {
let db = Firestore.firestore()
let userID = Auth.auth().currentUser?.uid
let username = usernameTextField.placeholder
Auth.auth().currentUser?.delete(completion: { (error) in
if error != nil {
print("ERROR MAIN SETTINGS 136")
} else {
db.collection("FollowerList").whereField("uid", isEqualTo: userID!).getDocuments { (snapshot, error) in
for snapshot in snapshot?.documents {
}
}
}
}
)}
My Database has a collection "FollowerList", with documents named with the User's UID. Within these documents is a "uid" field, with the value of the User's UID.
Any help would be massively appreciated.
This should do the Job:
func deleteAccountButtonIsTapped(_ sender: Any) {
let db = Firestore.firestore()
let userID = Auth.auth().currentUser?.uid
let username = usernameTextField.placeholder
Auth.auth().currentUser?.delete(completion: { (error) in
if error != nil {
print("ERROR MAIN SETTINGS 136")
} else {
db.collection("FollowerList").whereField("uid", isEqualTo: userID!).getDocuments { (snapshot, error) in
if let snapshot = snapshot?.documents {
for doc in snapshot {
//Do delete
db.collection("FollowerList").document(doc.documentID).updateData([
"fieldToDelete": FieldValue.delete(),
]) { err in
if let err = err {
print("Error updating document: \(err)")
} else {
print("Document successfully updated")
}
}
}
}
}
}
}
)}
One would think it could work like this:
But it doesn't as a value of type 'QueryDocumentSnapshot' has no member 'updateData'.
func deleteAccountButtonIsTapped(_ sender: Any) {
let db = Firestore.firestore()
let userID = Auth.auth().currentUser?.uid
let username = usernameTextField.placeholder
Auth.auth().currentUser?.delete(completion: { (error) in
if error != nil {
print("ERROR MAIN SETTINGS 136")
} else {
db.collection("FollowerList").whereField("uid", isEqualTo: userID!).getDocuments { (snapshot, error) in
if let snapshot = snapshot?.documents {
for doc in snapshot {
// How one would think it works but it doesnt
doc.updateData([
"capital": FieldValue.delete(),
]) { err in
if let err = err {
print("Error updating document: \(err)")
} else {
print("Document successfully updated")
}
}
}
}
}
}
}
)}
See this page for further information:
https://firebase.google.com/docs/firestore/manage-data/delete-data#swift

Get firebase server time cloud function

I'm trying to write a function that I can call on my iOS app to return me the current (or approximate) firebase server time.
I couldn't get any success with the following attempts. am I doing something wrong?
Obs.: The second one return an object of type FIRHTTPSCallableResult and I couldn't find a way to parse it, neither see the content into it to be sure if it worked.
exports.currentTime = functions.https.onRequest((req, res) => {
res.send({"timestamp":new Date().getTime()})
});
exports.currentTimeTwo = functions.https.onCall((data, context) => {
return {"timestamp":new Date().getTime()};
});
exports.currentTimeThree = functions.https.onRequest((req, res) => {
const data = {"timestamp":new Date().getTime()}
res.send(data)
});
iOS Code:
static func getServerTime(){
Functions.functions().httpsCallable("currentTimeTwo").call { (result, error) in
if let error = error as NSError? {
print("error \(error)")
if error.domain == FunctionsErrorDomain {
print("error domain: \(error.domain)")
let code = FunctionsErrorCode(rawValue: error.code)
print("error code: \(code)")
let message = error.localizedDescription
print("error message: \(message)")
let details = error.userInfo[FunctionsErrorDetailsKey]
print("details: \(details)")
}
}
print(result)
}
}
exports.currentTimeTwo = functions.https.onCall((data, context) => {
return {"timestamp":new Date().getTime()};
});
The function above is right. As Frank informed in the comments of my question, I was missing to access the property data from swift code result.
New Swift Code:
static func getServerTime(){
Functions.functions().httpsCallable("currentTimeTwo").call { (result, error) in
if let error = error as NSError? {
print("error \(error)")
if error.domain == FunctionsErrorDomain {
print("error domain: \(error.domain)")
let code = FunctionsErrorCode(rawValue: error.code)
print("error code: \(code)")
let message = error.localizedDescription
print("error message: \(message)")
let details = error.userInfo[FunctionsErrorDetailsKey]
print("details: \(details)")
}
}
let resultFireBase = result as! HTTPSCallableResult
print(result?.data)
}
}
}
Only the functions.https.onCall in your Cloud Functions code matches with the Functions.functions().httpsCallable in your Swift code, so the other ones are meaningless here.
When you call the httpsCallable, you get a FIRHTTPSCallableResult whose data property contains whatever your Cloud Function returns, as long as it's a valid JSON type, which your new Date().getTime() seems to be

Firestore not returning query Swift 5 Xcode 11.3

I'm currently trying to develop an ios application using Firestore, and when querying the database, I'm getting no results, as neither the if/else block execute. I'm wondering what is going wrong here...
db.collection("users").whereField("uid", isEqualTo: uid).getDocuments() { (querySnapshot, error) in
if let error = error {
print("Error getting documents: \(error.localizedDescription)")
} else {
for document in querySnapshot!.documents {
weight = document.data()["weight"]! as? Double
}
}
}
Database file structure
Update: I make a call to the database in an earlier method, and this properly returns the user's first name (when I add the weight, it also returns the correct value). But any subsequent calls fail to return anything. Hopefully that info helps.
I have the same Firestore structure like you, and this works for me:
func test() {
var accessLevel: Double?
let db = Firestore.firestore()
db.collection("users").whereField("uid", isEqualTo: UserApi.shared.CURRENT_USER_UID!).getDocuments() { (querySnapshot, error) in
if let error = error {
print("Error getting documents: \(error.localizedDescription)")
} else {
for document in querySnapshot!.documents {
accessLevel = document.data()["accessLevel"]! as? Double
print(accessLevel!)
}
}
}
}
Current uid:
// Actual logged-in User
var CURRENT_USER_UID: String? {
if let currentUserUid = Auth.auth().currentUser?.uid {
return currentUserUid
}
return nil
}
Hope it helps.

How do I detect if a Firebase Storage file exists?

I'm writing a Swift extension on FIRStorageReference to detect if a file exists or not. I am calling metadataWithCompletion(). If the completion block's optional NSError is not set, I think it's safe to assume that the file exists.
If the NSError is set, either something went wrong or the file doesn't exist. The storage documentation on handling errors in iOS states that FIRStorageErrorCodeObjectNotFound is the type of error that I should be checking, but is doesn't resolve (possibly Swiftified into a shorter .Name-style constant?) and I'm not sure what I should be checking it against.
I'd like to be calling completion(nil, false) if FIRStorageErrorCodeObjectNotFound is set somewhere.
Here's my code so far.
extension FIRStorageReference {
func exists(completion: (NSError?, Bool?) -> ()) {
metadataWithCompletion() { metadata, error in
if let error = error {
print("Error: \(error.localizedDescription)")
print("Error.code: \(error.code)")
// This is where I'd expect to be checking something.
completion(error, nil)
return
} else {
completion(nil, true)
}
}
}
}
Many thanks in advance.
You can check the error code like so:
// Check error code after completion
storageRef.metadataWithCompletion() { metadata, error in
guard let storageError = error else { return }
guard let errorCode = FIRStorageErrorCode(rawValue: storageError.code) else { return }
switch errorCode {
case .ObjectNotFound:
// File doesn't exist
case .Unauthorized:
// User doesn't have permission to access file
case .Cancelled:
// User canceled the upload
...
case .Unknown:
// Unknown error occurred, inspect the server response
}
}
Thats a simple code that I use to check if user has already got a user photo by hasChild("") method ,and the reference is here:
https://firebase.google.com/docs/reference/ios/firebasedatabase/interface_f_i_r_data_snapshot.html
hope this can help
let userID = FIRAuth.auth()?.currentUser?.uid
self.databaseRef.child("users").child(userID!).observeEventType(.Value, withBlock: { (snapshot) in
// Get user value
dispatch_async(dispatch_get_main_queue()){
let username = snapshot.value!["username"] as! String
self.userNameLabel.text = username
// check if user has photo
if snapshot.hasChild("userPhoto"){
// set image locatin
let filePath = "\(userID!)/\("userPhoto")"
// Assuming a < 10MB file, though you can change that
self.storageRef.child(filePath).dataWithMaxSize(10*1024*1024, completion: { (data, error) in
let userPhoto = UIImage(data: data!)
self.userPhoto.image = userPhoto
})
}
Swift 5
let storageRef = Storage.storage().reference().child("yourPath").child("\(someFile)") // eg. someVideoFile.mp4
print(storageRef.fullPath) // use this to print out the exact path that your checking to make sure there aren't any errors
storageRef.getMetadata() { (metadata: StorageMetadata?, error) in
if let error = error {
guard let errorCode = (error as NSError?)?.code else {
print("problem with error")
return
}
guard let err = StorageErrorCode(rawValue: errorCode) else {
print("problem with error code")
return
}
switch err {
case .objectNotFound:
print("File doesn't exist")
case .unauthorized:
print("User doesn't have permission to access file")
case .cancelled:
print("User cancelled the download")
case .unknown:
print("Unknown error occurred, inspect the server response")
default:
print("Another error occurred. This is a good place to retry the download")
}
return
}
// Metadata contains file metadata such as size, content-type.
guard let metadata = metadata else {
// an error occured while trying to retrieve metadata
print("metadata error")
return
}
if metadata.isFile {
print("file must exist becaus metaData is a file")
} else {
print("file for metadata doesn't exist")
}
let size = metadata.size
if size != 0 {
print("file must exist because this data has a size of: ", size)
} else {
print("if file size is equal to zero there must be a problem"
}
}
A shorter version without the detailed error check:
let storageRef = Storage.storage().reference().child("yourPath").child("\(someFile)")
storageRef.getMetadata() { (metadata: StorageMetadata?, error) in
if let error = error { return }
guard let metadata = metadata else { return }
if metadata.isFile {
print("file must exist because metaData is a file")
} else {
print("file for metadata doesn't exist")
}
let size = metadata.size
if size != 0 {
print("file must exist because this data has a size of: ", size)
} else {
print("if file size is equal to zero there must be a problem"
}
}

Resources