IOS SWIFT5: check is specific field odcument exist in firestore - swift

Seems there's no way to use boolean to return the checking result is exist or not. it only works in printing out the result...
func checkSeatAvailable() -> Bool{
var Exist: Bool
let g = DispatchGroup()
let resDate = ResDateTxt.text
let db = Firestore.firestore()
let docRef = db.collection("Reservations").whereField("resDate", isEqualTo: resDate!)
g.enter()
docRef.getDocuments() { (snapshot, error) in
if let snapshot = snapshot {
if snapshot.isEmpty {
print("Document does not exist")
Exist = true
g.leave()
} else {
print("Document data: \(snapshot) ")
Exist = false
g.leave()
}
}
}
g.notify(queue:.main) {}
print("\(Exist)")
return Exist
}

Add a completion Handler in your function as getDocuments function is asynchronous and return immediately. You can modify it like so.
func checkSeatAvailable(Completion:#escaping((Bool)->())){
//let g = DispatchGroup()
let resDate = ResDateTxt.text
let db = Firestore.firestore()
let docRef = db.collection("Reservations").whereField("resDate", isEqualTo: resDate!)
//g.enter()
docRef.getDocuments() { (snapshot, error) in
if let snapshot = snapshot {
if snapshot.isEmpty {
print("Document does not exist")
//g.leave()
} else {
print("Document data: \(snapshot) ")
Completion(true)
//g.leave()
}
}
}
//g.notify(queue:.main) {}
//print("\(Exist)")
//return Exist
}
And then call that function like that;
checkSeatAvailable { (boolValue) in
print(boolValue)
}

Related

Migrating to Combine causes Snapshotlistener to fail

I am migrating the codebase to Combine and the snapshotlistener isn't working i.e. when the document is updated in the back-end, it doesn't get reflected in the app.
This is where I call the service from the Controller:
#objc func loadPlans() {
guard let userEmail = userEmail else { return }
getPlansToken = PlanService.sharedInstance.queryPlans(userEmail: userEmail)
.receive(on: DispatchQueue.main)
.sink { (completion) in
switch completion {
case .failure(let error):
self.showAlert(alertTitle: "Error", message: error.localizedDescription)
case .finished:
print("Publisher stopped observing")
}
} receiveValue: { (mealplans) in
self.mealplanArray.removeAll()
self.mealplanArray = mealplans.map({return PlanViewModel(mealplan: $0)})
self.planTableView.reloadData()
self.refreshControl.endRefreshing()
}
}
And this is the service:
var listener : ListenerRegistration!
let db = Firestore.firestore()
static let sharedInstance = PlanService()
func queryPlans(userEmail: String) -> Future<[Mealplan], Error> {
var mealplans = [Mealplan]()
return Future { promise in
self.listener = self.db.collection("Meal_Plans")
.whereField("userId", isEqualTo: userEmail)
.order(by: "timeOfCreation", descending: true)
.addSnapshotListener({ (querySnapshot, error) in
guard let documents = querySnapshot?.documents else {
print("No mealplans")
return
}
mealplans = documents.compactMap { queryDocumentSnapshot -> Mealplan? in
return try? queryDocumentSnapshot.data(as: Mealplan.self)
}
promise(.success(self.updateTimeZone(source: mealplans)))
if let error = error {
promise(.failure(error))
}
})
}
}

Await function Swift/SwiftUI

I'm new in swift and SwiftUI and i have a big problem, I hope someone can help me. In fact, I have a function were I use a call to firebase Db, but the function end before the response of firebase. So is there any way to do like an await in swift ? I try to find by myself but everything I try doesn't work.
I put a sample of code, it's maybe going to be clearer.
extension SessionStore {
func checkReferralCode(){
let docRef = db.document(user.referredBy)
docRef.getDocument { (document, error) in
if let document = document, document.exists {
let otherUser = self.changeReferralUserInformation(dataDescription: document)
docRef.setData(otherUser)
self.user.moneyBalance = 1
return
} else {
print("No referral Code")
self.firestoreError = "referralCode_unvalid"
self.user.referredBy = ""
return
}
}
}
func doInscriptionInformation() {
if (self.user.referredBy != "") {
self.checkReferralCode()
if (self.firestoreError == "" ) {
/* it's always go in this way but 1 secs after the firestoreError change */
print("START UPLOAD")
self.determineUploadType()
} else {
return
}
}
}
The output it's gonna be :
$> START UPLOAD
$> No referral Code
I found an answer using semaphore, here the link of the tutorial https://medium.com/#roykronenfeld/semaphores-in-swift-e296ea80f860
Here the code
func checkReferralCode(semaphore: DispatchSemaphore){
let docRef = self.db.document(self.user.referredBy)
semaphore.wait()
docRef.getDocument { (document, error) in
if let document = document, document.exists {
let otherUser = self.changeReferralUserInformation(dataDescription: document)
docRef.setData(otherUser)
self.user.moneyBalance = 1
semaphore.signal()
} else {
self.firestoreError = "referralCode_unvalid"
self.user.referredBy = ""
semaphore.signal()
}
}
}
func doInscriptionInformation() {
let semaphore = DispatchSemaphore(value: 1)
DispatchQueue.global(qos: .userInteractive).async {
if (self.user.referredBy != "") {
self.checkReferralCode(semaphore: semaphore)
semaphore.wait()
if (self.firestoreError == "" ) {
self.determineUploadType()
}
else {
print("No good referral code")
}
semaphore.signal()
}
}
}
Hope that can help someone
make docRef a variable of the class instead of a local variable like so:
class WhateverClassNameYouHave: InheritedClass {
let docRef : DocRef (whatever type it is)
func checkCode() {
self.docRef = /* get firebase doc */
/* Call to firebase function */
docRef.getDocument { (document, error) in
/* Do my stuff*/
} else {
/*change variable to error*/
self.firestoreError = "referralCode_unvalid"
}
}
/*But the fonction end before the response of firebase so my self.firestoreError isn't update */
}

Update two fields at once with updateData

I am changing my online status with this code:
static func online(for uid: String, status: Bool, success: #escaping (Bool) -> Void) {
//True == Online, False == Offline
let db = Firestore.firestore()
let lastTime = Date().timeIntervalSince1970
let onlineStatus = ["onlineStatus" : status]
let lastTimeOnline = ["lastTimeOnline" : lastTime]
let ref = db.collection("users").document(uid)
ref.updateData(lastTimeOnline) {(error) in
if let error = error {
assertionFailure(error.localizedDescription)
success(false)
}
success(true)
}
ref.updateData(onlineStatus) {(error) in
if let error = error {
assertionFailure(error.localizedDescription)
success(false)
}
success(true)
}
}
I update the lastTimeOnline and the onlineStatus.
I listen to this updates via:
// Get the user online offline status
func getUserOnlineStatus(completion: #escaping (Dictionary<String, Any>) -> Void) {
let db = Firestore.firestore()
db.collection("users").addSnapshotListener { (querySnapshot, error) in
guard let snapshot = querySnapshot else {
print("Error fetching snapshots: \(error!)")
return
}
snapshot.documentChanges.forEach { diff in
if (diff.type == .modified) {
//GETS CALLED TWICE BUT I ONLY WANT ONCE
print("modified called..")
guard let onlineStatus = diff.document.get("onlineStatus") as? Bool else {return}
guard let userId = diff.document.get("uid") as? String else {return}
var userIsOnline = Dictionary<String, Any>()
userIsOnline[userId] = [onlineStatus, "huhu"]
completion(userIsOnline)
}
}
}
}
The problem is now, since I use ref.updateData twice, my SnapshotListener .modified returns the desired data twice.
How can I update two fields in a single call, so my .modified just return one snapshot?
You can try to combine them
let all:[String:Any] = ["onlineStatus" : status ,"lastTimeOnline" : lastTime]
let ref = db.collection("users").document(uid)
ref.updateData(all) {(error) in
if let error = error {
assertionFailure(error.localizedDescription)
success(false)
}
success(true)
}

swift firestore check if documents exists

using swift and firestore I want to check the "Taken User Names" collection to see if a username has been taken and if it has alert the user it taken otherwise if it's still available I want to create the file.
The gist of what I want to do is outlined below, I can save the data no problem though its the checking to see if its document exists then taking action that I cannot figure out
func nextButtonPressed(){
let db = Firestore.firestore()
if usernameTextField.text != ""{
guard let username = usernameTextField.text else { return }
let docRef = db.collection("Taken User Names").document(username)
// check if username exists{
//if exists alert user "sorry user name taken
} else {
// if user name doesn't exist
db.collection("Taken User Names").document("trinidad")
.setData(["Taken User Name" : (username)]) {
(error: Error?) in
if let error = error {
print("\(error.localizedDescription)")
} else {
print("document was succesfully created and written")
}
}
}
}
In a cleaner way:
let docRef = db.collection("collection").document("doc")
docRef.getDocument { (document, error) in
if document.exists {
print("Document data: \(document.data())")
} else {
print("Document does not exist")
}
}
func nextButtonPressed(){
let db = Firestore.firestore()
nextButton.isEnabled = false
if usernameTextField.text != ""{
guard let username = usernameTextField.text else { return }
guard let uid = Auth.auth().currentUser?.uid else { return }
let docRef = db.collection("Taken User Names").document(username)
docRef.getDocument { (document, error) in
if let document = document {
if document.exists{
print("Document data: \(document.data())")
self.alertTheUser(title: "Username Taken", message: "please choose again")
self.nextButton.isEnabled = true
} else {
print("Document does not exist")
}
}
}
}
}
try the following:
let db = Firestore.firestore()
guard let username = userNameTextField.text else { return }
let docRef = db.collection("users").whereField("username", isEqualTo: username).limit(to: 1)
docRef.getDocuments { (querysnapshot, error) in
if error != nil {
print("Document Error: ", error!)
} else {
if let doc = querysnapshot?.documents, !doc.isEmpty {
print("Document is present.")
}
}
}

Return a document with query in firestore

I want to return a document from a function using Firestore. Here is my query:
public class func getProductsInShoppingList(name:String = "Default") -> DocumentSnapshot?{
let db = Firestore.firestore()
let defaults = UserDefaults.standard
let userId: String! = defaults.string(forKey: "UserUUID")
var doc:DocumentSnapshot?
db.collection("shoppingLists")
.whereField("users." + userId, isEqualTo: true)
.whereField("name", isEqualTo: name)
.limit(to: 1)
.getDocuments() { (querySnapshot, err) in
if let err = err {
print("Error getting documents: \(err)")
} else {
for document in querySnapshot!.documents {
print("\(document.documentID) => \(document.data())")
doc = document
break
}
}
}
return doc
}
This is obviously an async operation. What is the proper way to return and use the document. Notification? Call back? Thanks
as in DidLoad just add a observer as
override func viewDidLoad()
{
super.viewDidLoad()
//Add notification Observer to get status out of Async mode
NotificationCenter.default.addObserver(self, selector: #selector(loadList), name: NSNotification.Name(rawValue: "DocumentReceived"), object: nil)
}
Now in your function
public class func getProductsInShoppingList(name:String = "Default") -> DocumentSnapshot?{
let db = Firestore.firestore()
let defaults = UserDefaults.standard
let userId: String! = defaults.string(forKey: "UserUUID")
var doc:DocumentSnapshot?
db.collection("shoppingLists")
.whereField("users." + userId, isEqualTo: true)
.whereField("name", isEqualTo: name)
.limit(to: 1)
.getDocuments() { (querySnapshot, err) in
if let err = err {
print("Error getting documents: \(err)")
} else {
for document in querySnapshot!.documents {
print("\(document.documentID) => \(document.data())")
doc = document
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "DocumentReceived"), object: nil)
break
}
}
}
return doc
}
When Notification Observer get that notification - Thus , the required function will be called itself
#objc func loadList(){
//load data here
}
Note - Do not forget to remove added Observer When it is not required
Second Option
Load Data When Operation is completed
public class func getProductsInShoppingList(name:String = "Default") -> DocumentSnapshot?{
let db = Firestore.firestore()
let defaults = UserDefaults.standard
let userId: String! = defaults.string(forKey: "UserUUID")
var doc:DocumentSnapshot?
db.collection("shoppingLists")
.whereField("users." + userId, isEqualTo: true)
.whereField("name", isEqualTo: name)
.limit(to: 1)
.getDocuments() { (querySnapshot, err) in
if let err = err {
print("Error getting documents: \(err)")
} else {
for document in querySnapshot!.documents {
print("\(document.documentID) => \(document.data())")
doc = document
DispatchQueue.main.async {
//Load data here
}
break
}
}
}
return doc
}