I am trying to use multithreaded operations with OperationQueue in my App.
Indeed, I would like to fetch all my data from all the different webservices during the launch, so that everything is ready to be use when I arrive on the Home page.
But I can't figure out how to properly do it.
Here is some code to visualize it :
class LauncherService {
weak var serviceForecast: ForecastServiceProtocol?
weak var serviceRadar: RadarServiceProtocol?
weak var serviceWarning: WarningServiceProtocol?
weak var serviceRain: RainServiceProtocol?
init(serviceForecast: ForecastServiceProtocol = ForecastService.shared,
serviceRadar: RadarServiceProtocol = RadarService.shared,
serviceWarning: WarningServiceProtocol = WarningService.shared,
serviceRain: RainServiceProtocol = RainService.shared) {
self.serviceForecast = serviceForecast
self.serviceRadar = serviceRadar
self.serviceWarning = serviceWarning
self.serviceRain = serviceRain
}
func requestAllServices(forCity city: City, completion: ((Result<Bool, ErrorResult>) -> Void)? = nil) {
if let lat = city.lat,
let lon = city.lon {
self.fetchForecast(lat: lat, lon: lon)
self.fetchRain(lat: lat, lon: lon)
}
self.fetchWarning()
self.fetchRadar()
}
}
I would like to wait until all the services have done their work before "notify" the HomePage that evertyhing is ready to go
.
Here is the implementation of the methods from all the different ServiceProtocol :
I will show you only one, but the implementation is exactly the same for all the services :
func fetchForecast(lat: String, lon: String, completion: ((Result<Bool, ErrorResult>) -> Void)? = nil) {
guard let service = serviceForecast else {
completion?(Result.failure(ErrorResult.custom(string: "Missing service")))
return
}
service.fetchForecast(lat: lat, lon: lon) { result in
DispatchQueue.main.async {
switch result {
case .success(let forecast) :
// DO something with this Forecast Model Object
// Add it in an array on return in completion
// But I would like to regroup all the different Model Objects from all my services.
completion?(Result.success(true))
break
case .failure(let error) :
print("Something went wrong - \(error)")
completion?(Result.failure(error))
break
}
}
}
}
I have read that OperationQueue is the solution to adopt here for my use case.
Should I implement something like this ?
let queue = OperationQueue()
for service in services {
queue.addOperation {
self.fetch()
}
}
queue.waitUntilAllOperationsAreFinished()
You can use DispatchGroup
for that:
let dispatchGroup = DispatchGroup()
dispatchGroup.enter()
fetchForecast {
dispatchGroup.leave()
}
dispatchGroup.enter()
fetchRain {
dispatchGroup.leave()
}
dispatchGroup.notify(queue: .main) {
print("All done")
}
The last block will execute when both of the request have finished loading.