0

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?

thatnaoki
  • 3
  • 2

1 Answers1

0

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)
            }
        }
    }
}
thatnaoki
  • 3
  • 2