Await function Swift/SwiftUI - swift

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 */
}

Related

How can I fix "Argument passed to call that takes no arguments" on function in Swift using FirebaseFirestoreSwift?

func fetchUser() {
guard let uid = userSession?.uid else { return }
Firestore.firestore().collection("users").document(uid).getDocument { snapshot, _ in
guard let user = try? snapshot?.data(as: User.self) else { return }
self.currentUser = user
}
}
The error is thrown on the "guard let user" line. Any idea on how to fix this error? Also, I am not using Cocoapods ... I am using Firebase from the github sdk
You need to ensure that you have:
import FirebaseFirestoreSwift
With this the data(as: ) function, it should work.
it seems you are missing the "documents" step, could you try something like this:
func fetchUser() {
guard let uid = userSession?.uid else { return }
Firestore.firestore().collection("users").document(uid).getDocument { snapshot, _ in
guard let docs = snapshot else { return }
for doc in docs.documents {
let user = doc.data(as: User.self)
self.currentUser = user
// break as required or just get the first in documents
}
}
}

#Published is not getting updated, State problem? - SwiftUI

Right now I have to call the function (calculatePortfolioGrossBalance) 3 times for the value to update, what am I doing wrong in the state logic?
In the code below, when I call in an init the function calculatePortfolioGrossBalance() it returns empty [], I have to call it 3 times for the value to update, However... if I print the values of getTokenBalancesModel in the line DispatchQueue.main.async { I can see the values are there, so how come in calculatePortfolioGrossBalance are not?
final class TokenBalancesClassAViewModel: ObservableObject {
#Published var getTokenBalancesModel: [TokenBalancesItemsModel] = [TokenBalancesItemsModel]()
#Published var portfolioGrossBalance: String = "0.0"
func calculatePortfolioGrossBalance() {
getTokenBalances()
DispatchQueue.main.async {
var totalBalance: Double = 0
for item in self.getTokenBalancesModel {
totalBalance += Double(item.quote!)
}
self.portfolioGrossBalance = String(format:"%.2f", totalBalance)
print(self.portfolioGrossBalance)
}
}
func getTokenBalances() {
guard let url = URL(string: "someUrlHeidiGaveMe") else {
print("Invalid URL")
return
}
print("Calling getTokenBalances() ...")
AF.request(url, method: .get).validate().responseData(completionHandler: { data in
do {
guard let data = data.data else {
print("Response Error:", data.error as Any)
return
}
let apiJsonData = try JSONDecoder().decode(TokenBalancesModel.self, from: data)
DispatchQueue.main.async {
self.getTokenBalancesModel = apiJsonData.data.items
}
} catch {
print("ERROR:", error)
}
})
}
}
you need to read up on using asynchronous functions, how to set them up and how to use them. This is important. Try something like this (untested):
final class TokenBalancesClassAViewModel: ObservableObject {
#Published var getTokenBalancesModel: [TokenBalancesItemsModel] = [TokenBalancesItemsModel]()
#Published var portfolioGrossBalance: String = "0.0"
func calculatePortfolioGrossBalance() {
getTokenBalances() { isGood in
if isGood {
var totalBalance: Double = 0
for item in self.getTokenBalancesModel {
totalBalance += Double(item.quote!)
}
self.portfolioGrossBalance = String(format:"%.2f", totalBalance)
print(self.portfolioGrossBalance)
}
}
}
func getTokenBalances(completion: #escaping (Bool) -> Void) {
guard let url = URL(string: "someUrlHeidiGaveMe") else {
print("Invalid URL")
completion(false)
return
}
print("Calling getTokenBalances() ...")
AF.request(url, method: .get).validate().responseData(completionHandler: { data in
do {
guard let data = data.data else {
print("Response Error:", data.error as Any)
completion(false)
return
}
let apiJsonData = try JSONDecoder().decode(TokenBalancesModel.self, from: data)
DispatchQueue.main.async {
self.getTokenBalancesModel = apiJsonData.data.items
completion(true)
}
} catch {
print("ERROR:", error)
completion(false)
}
})
}
}

IOS SWIFT5: check is specific field odcument exist in firestore

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)
}

Struggling with basic Swift logic (Working with constants, variables and loops)

I want to use 2 Strings that I get with those two "loops" from firebase and use them in another "loop" to upload them with a bunch of other Information.
My problem is, that I somehow can't get the values of fullname and pfp that I downloaded, into the upload to firebase.
Any ideas on how to solve this issue?
func sendToFire(){
let combined = "\(userID)" + "\(number)"
let docRef = db.collection("posts").document(combined)
let description = self.textPost.text
let nameRef = db.collection("users").document(userID)
var fullname = ""
var pfp = ""
if fireImage == nil {
nameRef.getDocument { (document, error) in
if let document = document{
fullname = document.get("fullname") as! String
}else{
print("Coulnt get fullname")
}
}
nameRef.getDocument { (document, error) in
if let document = document{
pfp = document.get("profileimage") as! String
}else{
print("Couldn't get profileimage")
}
}
docRef.getDocument { (document, error) in
if let document = document, document.exists {
print("Post ID already taken")
} else {
print("Post Document gets created")
self.db.collection("posts").document(combined).setData([
"description": description!,
"likes": self.likes,
"postType": 0,
"profileImage": pfp,
"time": self.date,
"uid": self.userID,
"username": fullname
]) { err in
if let err = err {
print("Error writing document: \(err)")
} else {
print("Post Document successfully written!")
}
}
}
}
}
}
Add document.exists in the if let
nameRef.getDocument { (document, error) in
if let document = document, document.exists{
fullname = document.get("fullname") as! String
}else{
print("Coulnt get fullname")
}
}
nameRef.getDocument { (document, error) in
if let document = document, document.exists{
pfp = document.get("profileimage") as! String
}else{
print("Couldn't get profileimage")
}
}
Check the actual key names in the response fullname and profileimage.

Wait for query to fetch Firestore data before continue

I would like to fetch the user/author of the article into a computed property. But this never happens, below code would print 1, 2, 4.
struct Article {
private let database = Firestore.firestore()
var authorId: String
var message: String
var author: User? {
print(1)
var fetchedAuthor: User?
let semaphore = DispatchSemaphore(value: 0)
print(2)
database.collection("users").document(authorId).getDocument { (document, error) in
print(3)
guard let user = document.flatMap({User(dictionary: $0.data())}) else {
print("Could not get author: \(error.debugDescription)")
semaphore.signal()
return
}
fetchedAuthor = user
semaphore.signal()
}
print(4)
semaphore.wait()
print(5)
return fetchedAuthor
}
}
Adding pseudo/ sample code to the comment by #Connor, since I just went through this.
extension User {
static func getBy(documentID id: String, completion: #escaping (User)->()) {
database.collection("users").document(id).getDocument { (document, error) in
guard let user = document.flatMap({User(dictionary: $0.data())}) else {
print("Could not get author: \(error.debugDescription)")
return
}
completion(user)
}
}
Then in your Article struct:
struct Article {
....
init(){
....
// Get the user
User.getBy(documentID: self.userID) { user in
self.author = user
}
}
}
semaphore.wait() //is for requesting the resource(semaphore)
semaphore.singal() //is for releasing the resource(semaphore)
Change code as per below.
var author: User? {
print(1)
var fetchedAuthor: User?
let semaphore = DispatchSemaphore(value: 1)
print(2)
database.collection("users").document(authorId).getDocument { (document, error) in
semaphore.wait()
print(3)
guard let user = document.flatMap({User(dictionary: $0.data())}) else {
print("Could not get author: \(error.debugDescription)")
return
}
fetchedAuthor = user
semaphore.signal()
}
print(4)
print(5)
return fetchedAuthor
}