在 Swift 中遇到完成处理程序和闭包问题

Having Trouble With Completion Handlers and Closures in Swift

背景

下面的函数调用两个函数,它们都访问 API、检索 JSON 数据、解析数据等,然后获取该数据并将对象变量的值填充到我的视图控制器 class。

func requestWordFromOxfordAPI(word: String, completion: (_ success: Bool) -> Void) {
        oxfordAPIManager.fetchDictData(word: word)
        oxfordAPIManager.fetchThesData(word: word)

        completion(true)
    }

通常情况下,如果只有一个函数获取数据,而我想调用一个新函数来接收数据结果并对其进行处理,我会使用委托方法并在数据抓取功能。

例如:

在这里,我从我的 firebase 数据库中获取数据,如果检索数据成功,我调用 self.delegate?.populateWordDataFromFB(result: combinedModel)。由于闭包发生在单独的线程上,这确保了 populateWordDataFromFB 函数 运行 仅在检索数据完成后才执行。如果我错了,请纠正我。我最近才了解到这一点,并且仍在努力了解全貌。

    func readData(word: String) {

        let docRef = db.collection(K.FBConstants.dictionaryCollectionName).document(word)

        docRef.getDocument { (document, error) in
            let result = Result {
                try document.flatMap {
                    try [=12=].data(as: CombinedModel.self)
                }
            }
            switch result {
            case .success(let combinedModel):
                if let combinedModel = combinedModel {
                    self.delegate?.populateWordDataFromFB(result: combinedModel)
                } else {
                    self.delegate?.fbDidFailWithError(error: nil, summary: "\(word) not found, requesting from OxfordAPI")
                    self.delegate?.requestWordFromOxfordAPI(word: word, completion: { (success) in
                        if success {
                            self.delegate?.populateWordDataFromOX()
                        } else {print("error with completion handler")}
                    })
                }
            case .failure(let error):
                self.delegate?.fbDidFailWithError(error: error, summary: "Error decoding CombinedModel")
            }
        }
    }

还要注意上面的代码,如果数据不在 firebase 中,我会调用下面的委托方法,这是我 运行 解决我的问题的地方。

self.delegate?.requestWordFromOxfordAPI(word: word, completion: { (success) in
                        if success {
                            self.delegate?.populateWordDataFromOX()
                        } else {print("error with completion handler")}
                    })

我的问题

我苦恼的是 oxfordAPIManager.fetchDictData(word: word)oxfordAPIManager.fetchThesData(word: word) 函数都有闭包。

这些函数的主体如下所示:

        if let url = URL(string: urlString) {
            var request = URLRequest(url: url)
            request.addValue(K.APISettings.acceptField, forHTTPHeaderField: "Accept")
            request.addValue(K.APISettings.paidAppID , forHTTPHeaderField: "app_id")
            request.addValue(K.APISettings.paidAppKey, forHTTPHeaderField: "app_key")

            let session = URLSession.shared
            _ = session.dataTask(with:request) { (data, response, error) in
                if error != nil {
                    self.delegate?.apiDidFailWithError(error: error, summary: "Error performing task:")
                    return
                }

                if let safeData = data {
                    if let thesaurusModel = self.parseThesJSON(safeData) {
                        self.delegate?.populateThesData(thesModel: thesaurusModel, word: word)
                    }
                }
            }
            .resume()
        }  else {print("Error creating thesaurus request")}

我假设这两个函数 运行 在后台的不同线程上运行。我的目标是在 oxfordAPIManager.fetchDictData(word: word)oxfordAPIManager.fetchThesData(word: word) 函数 运行 后调用另一个函数。这两个函数将填充我将在新函数中使用的视图控制器中的对象变量的值。我不希望在用正确的数据填充视图控制器中的对象变量之前调用新函数,因此我尝试实现一个完成处理程序。完成处理函数被调用 BEFORE 这两个函数终止,所以当新函数试图访问视图控制器中的对象变量时,它是空的。

这是我第一次尝试实现完成处理程序,我尝试关注其他一些堆栈溢出帖子,但没有成功。另外,如果这是错误的方法,请也告诉我。抱歉解释冗长,感谢您的任何意见。

为此使用DispatchGroup

示例:

创建 DispatchGroup,

let group = DispatchGroup()

requestWordFromOxfordAPI(word: completion:)方法修改为,

func requestWordFromOxfordAPI(word: String, completion: @escaping (_ success: Bool) -> Void) {
    fetchDictData(word: "")
    fetchThesData(word: "")
    group.notify(queue: .main) {
        //code after both methods are executed
        print("Both methods executed")
        completion(true)
    }
}

fetchDictData(word:)fetchThesData(word:)方法的相关地方调用DispatchGroupenter()leave()方法

func fetchDictData(word: String) {
    group.enter()
    URLSession.shared.dataTask(with: url) { (data, response, error) in
        //your code
        group.leave()
    }.resume()
}

func fetchThesData(word: String) {
    group.enter()
    URLSession.shared.dataTask(with: url) { (data, response, error) in
        //your code
        group.leave()
    }.resume()
}

最后一次通话 requestWordFromOxfordAPI(word: completion:)

requestWordFromOxfordAPI(word: "") { (success) in
    print(success)
}