Async Future Promise not working in Swift 5 - swift

I'm trying to implement an async call to a function loadUserFromFirebase and populate and return an array based on user attributes that I then use to write to a firebase collection.
I'm trying to use the Combine framework from Swift using future and promises but for some reason, the receiveCompletion and receiveValue don't get called and thus I get an empty array from my async function call.
Here is my code:
var cancellable = Set<AnyCancellable>()
func loadUserFromFirebase(groupUserIds: [String], handler: #escaping ([groupMate]) -> Void) {
var groupmateID = 0
var groupMates : [groupMate] = []
print("groupUserIds: \(groupUserIds)" )
for groupUserUID in groupUserIds {
print("groupUserUID: \(groupUserUID)" )
let future = Future<groupMate, Never> { promise in
self.ref.collection("Users").document(groupUserUID).getDocument(){
(friendDocument, err) in
if let err = err {
print("Error getting documents \(err)")
} else {
// print("friendDocument: \(String(describing: friendDocument?.data()))" )
let groupUsername = (friendDocument?.data()?["username"]) as! String
let groupUID = (friendDocument?.data()?["uid"]) as! String
let groupName = (friendDocument?.data()?["name"]) as! String
let groupPic = (friendDocument?.data()?["imageurl"]) as! String
promise(.success(groupMate(id: groupmateID, uid: groupUID , name: groupName , username: groupUsername, pic: groupPic)))
}
groupmateID += 1
}
}
print("in receiveCompletion")
future.sink(receiveCompletion: { completion in
print("in receiveCompletion")
print(1, completion)
switch completion {
case .failure(let error):
print(3, error)
handler([])
return
case .finished:
break
}
},
receiveValue: {
print("in receiveValue")
groupMates.append($0)
print(groupMates)
handler(groupMates)
}).store(in: &cancellable)
}
}
func creategroup(groupName: String){
addedTogroupUsers.append(self.uid)
print("here111")
loadUserFromFirebase(groupUserIds: addedTogroupUsers) { groupMates in
print("here222")
let groupData: [String: Any] = [
"groupName": "\(groupName)",
"groupmates": groupMates
]
print("here333 \(groupData)")
print("groupMates are \(self.groupMates)")
var groupref: DocumentReference? = nil
groupref = self.ref.collection("groups").addDocument(data: groupData) { err in
if let err = err {
print("Error adding document: \(err)")
} else {
print("Document added with ID: \(groupref!.documentID)")
for addedgroupUser in self.addedTogroupUsers {
self.ref.collection("Users").document(addedgroupUser).updateData([
"groups": FieldValue.arrayUnion([groupref!.documentID])
])
}
}
}
print("groupName is \(groupName) and addedTogroup are \(self.addedTogroupUsers)")
}
}
I'm trying to see if AnyCancellable is the way to go but since I'm using a chained array of future promises, I'm not sure how to implement it. Please let me know how you'd solve this problem so that the array does get populated since the documents do exist and the print inside the method call work but the groupMates array in the createGroup function prints an empty array afterwards. Thanks!
Edit: Added AnyCancallable to Code along with completion handler as suggested

dealing with async functions can be tricky. You are getting an empty array, because you are returning too early, in loadUserFromFirebase. Try this approach (untested) using the old style closure:
func loadUserFromFirebase(groupUserIds: [String], handler: #escaping ([groupMate]) -> Void) { // <-- here
var groupmateID = 0
var groupMates : [String] = []
print("groupUserIds: \(groupUserIds)" )
for groupUserUID in groupUserIds {
print("groupUserUID: \(groupUserUID)" )
let future = Future<groupMate, Never> { promise in
self.ref.collection("Users").document(groupUserUID).getDocument(){ (friendDocument, err) in
if let err = err {
print("Error getting documents \(err)")
} else {
print("friendDocument: \(String(describing: friendDocument?.data()))" )
let groupUsername = (friendDocument?.data()?["username"]) as! String
let groupUID = (friendDocument?.data()?["uid"]) as! String
let groupName = (friendDocument?.data()?["name"]) as! String
let groupPic = (friendDocument?.data()?["imageurl"]) as! String
promise(.success(groupMate(id: groupmateID, uid: groupUID , name: groupName , username: groupUsername, pic: groupPic)))
}
groupmateID += 1
}
}
future.sink(receiveCompletion: { completion in
print("in receiveCompletion")
print(1, completion)
switch completion {
case .failure(let error):
print(3, error)
handler([]) // <-- here
return // <-- here
case .finished:
break
}
},
receiveValue: {
print("in receiveValue")
groupMates.append($0)
print(groupMates)
handler(groupMates) // <-- here
})
}
// <-- do not return here
}
func creategroup(groupName: String) {
addedTogroupUsers.append(self.uid)
// -- here wait until you get the data
loadUserFromFirebase(groupUserIds: addedTogroupUsers) { groupMates in
let groupData: [String: Any] = [
"groupName": "\(groupName)",
"groupmates": groupMates // <-- here
]
print("groupMates are \(self.groupMates)")
var groupref: DocumentReference? = nil
groupref = ref.collection("groups").addDocument(data: groupData) { err in
if let err = err {
print("Error adding document: \(err)")
} else {
print("Document added with ID: \(groupref!.documentID)")
for addedgroupUser in self.addedTogroupUsers {
self.ref.collection("Users").document(addedgroupUser).updateData([
"groups": FieldValue.arrayUnion([groupref!.documentID])
])
}
}
}
print("groupName is \(groupName) and addedTogroup are \(addedTogroupUsers)")
}
}
Note, if you are targeting ios 15, macos 12, you are far better served if you use the swift 5.5 async/await/task features. They really work well.
EDIT: trying to return all results
func loadUserFromFirebase(groupUserIds: [String], handler: #escaping ([groupMate]) -> Void) {
var groupmateID = 0
var groupMates : [String] = []
print("groupUserIds: \(groupUserIds)" )
var arr: [Future<groupMate, Never>] = [Future<groupMate, Never>]()
var cancellable = Set<AnyCancellable>()
for groupUserUID in groupUserIds {
print("groupUserUID: \(groupUserUID)" )
let future = Future<groupMate, Never> { promise in
self.ref.collection("Users").document(groupUserUID).getDocument(){ (friendDocument, err) in
if let err = err {
print("Error getting documents \(err)")
} else {
print("friendDocument: \(String(describing: friendDocument?.data()))" )
let groupUsername = (friendDocument?.data()?["username"]) as! String
let groupUID = (friendDocument?.data()?["uid"]) as! String
let groupName = (friendDocument?.data()?["name"]) as! String
let groupPic = (friendDocument?.data()?["imageurl"]) as! String
promise(.success(groupMate(id: groupmateID, uid: groupUID , name: groupName , username: groupUsername, pic: groupPic)))
}
groupmateID += 1
}
}
arr.append(future)
}
Publishers.MergeMany(arr)
.collect()
.sink { _ in
print("-----> merging ")
} receiveValue: { value in
print("-----> value: \(value)")
groupMates = value // <--- maybe append?
print(groupMates)
handler(groupMates)
}
.store(in: &cancellable)
}

Related

Can't fetch correctly and update displayName in the Firebase

When registering through Apple Sign In in the Firebase -> document is created in the users collection with three default fields: uid, email and name, all fields are filled in automatically and saved in the database, as you can know.
The problem is:
Firstly, the name is not displayed in the app, and secondly, when you try to change the name, the name field changes inside the Firebase, and is also saved in the app itself until the moment it is closed, after opening the app, you need to change the name again so that it becomes displayed in the application.
Main for changing name through textfield:
#EnvironmentObject var userInfo: UserInfo
#State private var changeName = false
#State private var fullname = ""
HStack {
if !changeName {
TextField("Enter your name", text: $fullname)
.onReceive(fullname.publisher.collect()) {
self.fullname = String($0.prefix(15))
}
.submitLabel(.done)
} else {
Text(fullname)
}
Button(action: {
self.changeName.toggle()
FBFirestore.updateUserName(with: fullname, uid: userInfo.user.uid) { result in
switch result {
case .success:
print("success")
userInfo.user.name = fullname
presentationMode.wrappedValue.dismiss()
case .failure(let error):
print(error.localizedDescription)
}
}
}
){
Text(!changeName ? "Done" : "Edit")
}
.disabled(fullname.isEmpty)
}
UserInfo:
public class UserInfo: ObservableObject {
public enum FBAuthState {
case undefined, signedOut, signedIn
}
#Published public var isUserAuthenticated: FBAuthState
#Published public var user: FBUser
var authStateDidChangeListenerHandle: AuthStateDidChangeListenerHandle?
public init(isUserAuthenticated: Published<FBAuthState>
= Published<FBAuthState>.init(wrappedValue: .undefined),
user: Published<FBUser> = Published<FBUser>.init(wrappedValue: FBUser(uid: "",
name: "",
email: ""))) {
self._user = user
self._isUserAuthenticated = isUserAuthenticated
}
func configureFirebaseStateDidChange() {
authStateDidChangeListenerHandle = Auth.auth().addStateDidChangeListener({ (_, user) in
guard let user = user else {
self.isUserAuthenticated = .signedOut
return
}
self.isUserAuthenticated = .signedIn
FBFirestore.retrieveFBUser(uid: user.uid) { (result) in
switch result {
case .failure(let error):
print(error.localizedDescription)
case .success(let user):
self.user = user
}
}
})
}
}
FBUser:
public struct FBUser: Codable {
public let uid: String
public var name: String
let email: String
public init(uid: String, name: String, email: String) {
self.uid = uid
self.name = name
self.email = email
}
}
Firestore stuff with updateUserName func:
public enum FBFirestore {
static func retrieveFBUser(uid: String, completion: #escaping (Result<FBUser, Error>) -> Void) {
let reference = Firestore
.firestore()
.collection(FBKeys.CollectionPath.users)
.document(uid)
getDocument(for: reference) { result in
switch result {
case .success(let document):
do {
let user = try document.data(as: FBUser.self)
// guard let user = try document.data(as: FBUser.self) else {
// completion(.failure(FireStoreError.noUser))
// return
// }
completion(.success(user))
} catch {
completion(.failure(FireStoreError.noUser))
}
case .failure(let error):
completion(.failure(error))
}
}
}
static func updateUserName(with newName: String, uid: String, completion: #escaping (Result<Bool, Error>) -> Void) {
let user = Auth.auth().currentUser
let reference = Firestore.firestore().collection(FBKeys.CollectionPath.users)
.document(user!.uid)
reference.setData(["name": newName], merge: true) { err in
if let err = err {
completion(.failure(err))
return
}
completion(.success(true))
}
}
static func mergeFBUser(fbUser: FBUser, uid: String, completion: #escaping (Result<Bool, Error>) -> Void) {
let reference = Firestore
.firestore()
.collection(FBKeys.CollectionPath.users)
.document(uid)
do {
_ = try reference.setData(from: fbUser, merge: true)
completion(.success(true))
} catch {
completion(.failure(error))
}
}
fileprivate static func getDocument(for reference: DocumentReference,
completion: #escaping (Result<DocumentSnapshot, Error>) -> Void) {
reference.getDocument { (documentSnapshot, err) in
if let err = err {
completion(.failure(err))
return
}
guard let documentSnapshot = documentSnapshot else {
completion(.failure(FireStoreError.noDocumentSnapshot))
return
}
completion(.success(documentSnapshot))
}
}
}
So what could be the problem? Everything seems to be correct, but some trifle is missed, thanks for any information.

Swift - Fetch elements from Firestore

I have a problem with this function:
func fetchGameFromDB(completionHandler: #escaping ([GamesObject]) -> Void) {
db.collection("games").getDocuments { (querySnapshot, err) in
if let err = err {
print("Error: \(err)")
} else {
self.gameObject = []
for document in querySnapshot!.documents {
print("document \(document.data())")
if let name = document.data()["name"] as? String {
let docRef = self.db.collection("games").document(name)
docRef.getDocument { document, error in
if let document = document {
let data = document.data()
let name = data?["name"] as? String ?? ""
let urlStanding = data?["urlStanding"] as? String ?? ""
let img = data?["gameImg"] as? String ?? ""
let urlUpcoming = data?["urlUpcoming"] as? String ?? ""
self.gameObject.append(GamesObject(name: name, gameImg: img, urlStanding: urlStanding, urlUpcoming: urlUpcoming))
// here i have elements in gameObject
}
// here i have elements in gameObject
}
// here gameObject = []
}
// here gameObject = []
}
completionHandler(self.gameObject)
// here gameObject = []
}
}
}
I get my data well and I add it to my array but when I get to the completionHandler the array is empty.
I find solution, i check if gameObject.count == querySnapshot?.count then I use my completionHandler
func fetchGameFromDB(completionHandler: #escaping ([GamesObject]) -> Void) {
db.collection("games").getDocuments { (querySnapshot, err) in
if let err = err {
print("Error: \(err)")
} else {
self.gameObject = []
querySnapshot?.documents.forEach({ (document) in
if let name = document.data()["name"] as? String {
let docRef = self.db.collection("games").document(name)
docRef.getDocument { document, error in
if let document = document {
let data = document.data()
let name = data?["name"] as? String ?? ""
let urlStanding = data?["urlStanding"] as? String ?? ""
let img = data?["gameImg"] as? String ?? ""
let urlUpcoming = data?["urlUpcoming"] as? String ?? ""
self.gameObject.append(GamesObject(name: name, gameImg: img, urlStanding: urlStanding, urlUpcoming: urlUpcoming))
if self.gameObject.count == querySnapshot?.count {
completionHandler(self.gameObject)
}
}
}
}
})
}
}
}
the first answer there is no problem as long as the missing documents do not exist. but, that cannot be escaped if any of the documents are missing.
how about use to 'DispatchGroup' ?
func fetchGameFromDB(completionHandler: #escaping([GamesObject]) -> Void) {
db.collection("games").getDocuments { (querySnapshot, error) in
guard let docs = querySnapshot?.documents, !docs.isEmpty else {
if let error = error {
print(error)
}
return
}
let group = DispatchGroup()
docs.forEach { doc in
group.enter()
guard let name = doc.data()["name"] as? String else {
group.leave()
return
}
let docRef = self.db.collection("games").document(name)
docRef.getDocument { document, error in
if let document = document, let data = document.data() {
//do something...
gameObjects.append(GamesObject(...)) //init object
}
group.leave()
}
}
group.notify(queue: .main) {
completionHandler(gameObjects)
}
}
}

Am I using firebase api incorrectly?

Whenever paging in tableview, the view model is running fetchDataRx. It works well, but I don't think it's right to sort the entire data every time you paginate and call the addSnapShotListener. If my code is not correct, how can I correct it?
// MARK: ViewController.swift
timeLineTableView.rx.didScroll
.withLatestFrom(viewModel.activated)
.subscribe(onNext: { [weak self] isActivated in
if !isActivated {
guard let self = self else { return }
let position = self.timeLineTableView.contentOffset.y
if position > self.timeLineTableView.contentSize.height - 100 - self.timeLineTableView.frame.size.height {
self.viewModel.fetchPosts.onNext(())
}
}
})
.disposed(by: disposeBag)
//MARK: ViewModel.swift
let fetchPosts: AnyObserver<Void>
let fetching = PublishSubject<Void>()
fetchPosts = fetching.asObserver()
fetching
.do(onNext: { _ in activating.onNext(true) })
.withLatestFrom(posts)
.map { $0.count }
.flatMap{ (count) -> Observable<[post]> in
fireBaseService.fetchDataRx(startIdx: count) }
.map { $0.map { ViewPost(post: $0) } }
.do(onNext: { _ in activating.onNext(false) })
.do(onError: { err in error.onNext(err) })
.subscribe(onNext: { newPosts in
let oldData = posts.value
posts.accept(oldData + newPosts)
})
.disposed(by: disposeBag)
//MARK: FirebaseService.swift
protocol FirebaseServiceProtocol {
func fetchDataRx(startIdx: Int) -> Observable<[post]>
func fetchData(startIdx: Int, completion: #escaping (Result<[post], Error>) -> Void)
}
class FireBaseService: FirebaseServiceProtocol {
func fetchDataRx(startIdx: Int) -> Observable<[post]> {
return Observable.create { (observer) -> Disposable in
self.fetchData(startIdx: startIdx) { result in
switch result {
case .success(let data):
observer.onNext(data)
case .failure(let error):
observer.onError(error)
}
observer.onCompleted()
}
return Disposables.create()
}
}
func fetchData(startIdx: Int, completion: #escaping (Result<[post], Error>) -> Void) {
let db = Firestore.firestore()
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd HH:mm"
if startIdx == 0 {
DispatchQueue.global().async {
let first = db.collection("lolCourt")
.order(by: "date")
.limit(to: 8)
var nextPosts = [post]()
first.getDocuments() { (querySnapshot, error) in
if let error = error {
print("Error getting documents: \(error)")
} else {
for document in querySnapshot!.documents {
guard let url = document.data()["url"] as? String else {
continue
}
guard let champion1 = document.data()["champion1"] as? String else {
continue
}
guard let champion1Votes = document.data()["champion1Votes"] as? Double else {
continue
}
guard let champion2 = document.data()["champion2"] as? String else {
continue
}
guard let champion2Votes = document.data()["champion2Votes"] as? Double else {
continue
}
guard let text = document.data()["text"] as? String else {
continue
}
guard let date = document.data()["date"] as? Double else {
continue
}
nextPosts.append(post(url: url,
champion1: champion1,
champion1Votes: champion1Votes,
champion2: champion2,
champion2Votes: champion2Votes,
text: text,
date: formatter.string(from: Date(timeIntervalSince1970: date))))
}
}
completion(.success(nextPosts))
}
}
}
else {
DispatchQueue.global().async {
let first = db.collection("lolCourt")
.order(by: "date")
.limit(to: startIdx)
first.addSnapshotListener { (snapshot, error) in
guard let snapshot = snapshot else {
print("Error retrieving : \(error.debugDescription)")
return
}
guard let lastSnapshot = snapshot.documents.last else {
return
}
let next = db.collection("lolCourt")
.order(by: "date")
.start(afterDocument: lastSnapshot)
.limit(to: 8)
var nextPosts = [post]()
next.getDocuments() { (querySnapshot, error) in
if let error = error {
print("Error getting documents: \(error)")
} else {
for document in querySnapshot!.documents {
guard let url = document.data()["url"] as? String else {
continue
}
guard let champion1 = document.data()["champion1"] as? String else {
continue
}
guard let champion1Votes = document.data()["champion1Votes"] as? Double else {
continue
}
guard let champion2 = document.data()["champion2"] as? String else {
continue
}
guard let champion2Votes = document.data()["champion2Votes"] as? Double else {
continue
}
guard let text = document.data()["text"] as? String else {
continue
}
guard let date = document.data()["date"] as? Double else {
continue
}
nextPosts.append(post(url: url,
champion1: champion1,
champion1Votes: champion1Votes,
champion2: champion2,
champion2Votes: champion2Votes,
text: text,
date: formatter.string(from: Date(timeIntervalSince1970: date))))
}
}
completion(.success(nextPosts))
}
}
}
}
}
}
I think you are doing it wrong... I would expect to see something more like this:
class FireBaseService {
func getPage<T>(query: Query? = nil, build: #escaping ([String: Any]) -> T) -> Observable<([T], nextPage: Query?)> {
Observable.create { observer in
let db = Firestore.firestore()
let page = query ?? db.collection("lolCourt")
.order(by: "date")
.limit(to: 8)
let listener = page
.addSnapshotListener { snapshot, error in
guard let snapshot = snapshot else { observer.onError(error ?? RxError.unknown); return }
let items = snapshot.documents.map { build($0.data()) }
if let lastSnapshot = snapshot.documents.last {
let next = page
.start(afterDocument: lastSnapshot)
observer.onSuccess((items, nextPage: next))
}
else {
observer.onSuccess((items, nextPage: nil))
}
}
return Disposables.create { listener.remove() }
}
}
}
Use the above in your favorite state machine system. Here is an example using my CLE library.
// in view controller
let fireBaseService = FireBaseService()
let activityIndicator = ActivityIndicator()
let errorRouter = ErrorRouter()
func getPage(nextPage: Query?) -> Observable<([Post?], nextPage: Query?)> {
fireBaseService.getPage(query: nextPage, build: Post.init(dict:))
.rerouteError(errorRouter)
.trackActivity(activityIndicator)
}
let posts = cycle(
inputs: [
getPage(nextPage: nil).map(ViewModel.Input.response),
timeLineTableView.rx.reachedBottom(offset: 20).map(to: ViewModel.Input.next)
],
initialState: ([Post?](), nextPage: Query?.none),
environment: getPage(nextPage:),
reduce: ViewModel.reduce(state:input:getPage:)
)
.map { $0.0.compactMap { $0 } }
and the view model:
enum ViewModel {
enum Input {
case response([Post?], nextPage: Query?)
case next
}
static func reduce(state: inout ([Post?], nextPage: Query?), input: Input, getPage: #escaping (Query) -> Observable<([Post?], nextPage: Query?)>) -> Observable<Input> {
switch input {
case let .response(posts, nextPage):
state.0 += posts
state.nextPage = nextPage
case .next:
guard let nextPage = state.nextPage else { break }
return getPage(nextPage)
.map(Input.response)
}
return .empty()
}
}

Alamofire request not being executed after adding one parameter

I have this function. Whenever I comment the "v": 2 parameter it works like a charm, then I add it and when trying to call the function from my VC it doesn't get fired off. I think the problem is somewhere at the top. Like I said, when commenting the "v:2" parameter, everything goes smooth. I've checked my postman and the URL Request adding that parameter does give me the right response, but my function is not being called in my VC. Any ideas? Please I'm desperate ):
public func getBusinessBy(location loc: String, type: Int, loading: Bool, OnSuccess success: #escaping (_ businesses: [NSDictionary]) -> Void, OnFailed failed: #escaping (_ error: String) -> Void) {
if loading {
GlobalLoader.show()
}
let bUrl = HttpUtils.getBusinessesBy(type: type)
let params: Parameters = [
"location": loc,
"type": type,
"v": 2,
"params": Config.ENABLE_SEARCH ? Config.SEARCH_BUSINESS_PARAMS : Config.COMMON_BUSINESS_PARAMS,
]
Alamofire.request(bUrl, method: .get, parameters: params, encoding: URLEncoding.queryString, headers: [:]).responseJSON() { response in
switch response.result {
case .success(let val):
print("yaaaaay!")
if let res = val as? NSDictionary {
if let businesses = res.results() {
let allBusiesses = businesses.sorted(by: { (d1, d2) -> Bool in
(d1.object(forKey: "open") as! Bool) && !(d2.object(forKey: "open") as! Bool)
})
// Storing All Businesses to global
Session.sharedInstance.setAllBusiness(allBusiesses)
var tmpCat = [NSDictionary]()
var tmpPro = [NSDictionary]()
var tmpPMotion = [NSDictionary]()
var tmpPBusiness = [NSDictionary]()
for busin in allBusiesses {
guard let categories = busin["categories"] as? [NSDictionary] else {
return
}
tmpCat.append(contentsOf: categories)
var flag = false
for cat in categories {
if let prod = cat["products"] as? [NSDictionary] {
for p in prod {
var pClone = NSMutableDictionary()
pClone = p.mutableCopy() as! NSMutableDictionary
let pivot = NSMutableDictionary()
pivot["business_id"] = busin.getId()
pivot["business_name"] = busin.getName()
pivot["business_description"] = busin.getDescription()
pivot["business_enabled"] = busin.isOpened()
pivot["category_name"] = cat.getName()
pivot["category_description"] = cat.getDescription()
pivot["category_enabled"] = cat.isEnabled()
pClone["pivot"] = pivot
tmpPro.append(pClone)
if p.isFeatured() {
tmpPMotion.append(pClone)
if !flag {
tmpPBusiness.append(busin)
flag = true
}
}
}
}
}
}
Session.sharedInstance.setAllProduct(tmpPro)
Session.sharedInstance.setPromotions(tmpPMotion)
Session.sharedInstance.setPromBusinesses(tmpPBusiness)
Session.sharedInstance.setAllCategory(tmpCat)
// }
success(businesses)
}
} else {
failed(val as? String ?? "Passing error!")
}
if loading {
// SVProgressHUD.dismiss()
GlobalLoader.hide()
}
break
case .failure(let error):
print("naaaaay!")
failed(error.localizedDescription)
if loading {
// SVProgressHUD.dismiss()
GlobalLoader.hide()
}
break
}
}
}

How can I return a function only when the async call in a loop is completed in SwiftUI and Firebase?

I wanted to have a function that returns the list of reviews made by the user when a user is passed in as a parameter. However, I realize that the function is returning an empty array as the call to the database is only run after the function returns.
I did some search online and tried to use DispatchGroup but not sure if I'm applying it correctly.
Not sure if I'm being clear enough but here's a snippet of my code:
func getAllReviews(user: User) {
let reviewID = user.reviews
var reviews: [Review] = [Review]()
let group = DispatchGroup()
group.enter()
for i in reviewID {
print(i)
db.collection("reviews").document(i).getDocument {
(query, err) in
print("alive??")
DispatchQueue.main.async {
if err != nil {
print("hello")
} else {
print("did it reach")
let userId = query!.get("user id") as? String ?? ""
let star = query!.get("star") as? Int ?? -1
let value = query!.get("value") as? Int ?? 0
let comments = query!.get("comments") as? String ?? ""
print(comments)
reviews.append(Review(id: i,userId: userId, star: star, value: value, comments: comments))
print(reviews)
print("does it come")
}
}
group.leave()
}
}
group.notify(queue: .main) {
completion(reviews)
}
}
Would appreciate any advice, thank you in advance!
The enter line is at the wrong place, it must be inside the loop.
And the DispatchQueue.main closure is not needed
func getAllReviews(user: User) {
let reviewID = user.reviews
var reviews: [Review] = [Review]()
let group = DispatchGroup()
for i in reviewID {
print(i)
group.enter()
db.collection("reviews").document(i).getDocument {
(query, err) in
print("alive??")
if err != nil {
print("hello")
} else {
print("did it reach")
let userId = query!.get("user id") as? String ?? ""
let star = query!.get("star") as? Int ?? -1
let value = query!.get("value") as? Int ?? 0
let comments = query!.get("comments") as? String ?? ""
print(comments)
reviews.append(Review(id: i,userId: userId, star: star, value: value, comments: comments))
print(reviews)
print("does it come")
}
group.leave()
}
}
group.notify(queue: .main) {
completion(reviews)
}
}