I'm trying to fetch data and update core data based on the new updated API-Data.
I have this download function:
func download1(stock: String, completion: @escaping (Result<[Quote], NetworkError>) -> Void) {
var internalQuotes = [Quote]()
let downloadQueue = DispatchQueue(label: "com.app.downloadQueue")
let downloadGroup = DispatchGroup()
downloadGroup.enter()
let url = URL(string: API.quoteUrl(for: stock))!
NetworkManager<GlobalQuoteResponse>().fetch(from: url) { (result) in
switch result {
case .failure(let err):
print(err)
downloadQueue.async {
downloadGroup.leave()
}
case .success(let resp):
downloadQueue.async {
internalQuotes.append(resp.quote)
downloadGroup.leave()
}
}
}
downloadGroup.notify(queue: DispatchQueue.global()) {
completion(.success(internalQuotes))
DispatchQueue.main.async {
self.quotes.append(contentsOf: internalQuotes)
}
}
}
On the ContentView I try to implement an update function:
func updateAPI() {
for stock in depot.aktienKatArray {
download.download1(stock: stock.aKat_symbol ?? "") { _ in
//
}
for allS in download.quotes {
if allS.symbol == stock.aKat_symbol {
stock.aKat_currPerShare = Double(allS.price) ?? 0
}
}
}
PersistenceController.shared.saveContext()
}
My problem is that the for loop in the update function should only go on if the first part (download.download1) is finished with downloading the data from the API.
CodePudding user response:
Don't wait! Never wait!
DispatchGroup is a good choice – however nowadays I highly recommend Swift Concurrency – but it's at the wrong place.
.enter()
must be called inside the loop before the asynchronous task starts.leave()
must be called exactly once inside the completion handler of the asynchronous task (ensured by adefer
statement)
I know this code won't work most likely, but I merged the two functions to the correct DispatchGroup
workflow. I removed the custom queue because the NetworkManager
is supposed to do its work on a custom background queue
func updateAPI() {
var internalQuotes = [Quote]()
let downloadGroup = DispatchGroup()
for stock in depot.aktienKatArray {
downloadGroup.enter()
let url = URL(string: API.quoteUrl(for: stock))!
NetworkManager<GlobalQuoteResponse>().fetch(from: url) { result in
defer { downloadGroup.leave() }
switch result {
case .failure(let err):
print(err)
case .success(let resp):
internalQuotes.append(resp.quote)
for allS in download.quotes {
if allS.symbol == stock.aKat_symbol {
stock.aKat_currPerShare = Double(allS.price) ?? 0
}
}
}
}
}
downloadGroup.notify(queue: .main) {
self.quotes.append(contentsOf: internalQuotes)
PersistenceController.shared.saveContext()
}
}