Commit d5522f2c by Demid Merzlyakov

WdtWeatherSource: add checks to avoid updating one location multiple times simultaneously.

parent aa1bfd9d
......@@ -10,10 +10,12 @@ import CoreLocation
import XMLCoder
public enum WdtWeatherSourceError: Error {
case insufficientLocationInfo
case badUrl
case networkError(Error?)
case badServerResponse(Error?)
case dataMergeError(String)
case alreadyBeingUpdated
}
public class WdtWeatherSource: WeatherSource {
......@@ -21,10 +23,39 @@ public class WdtWeatherSource: WeatherSource {
private static let updateUrlMega = "https://1weather.onelouder.com/feeds/onelouder/mega.php"
private static let updateUrlMicro = "https://1weather.onelouder.com/feeds/onelouder2/fm.php"
/// This queue is needed to synchronize access to locationsBeingUpdated. Also, to make logging more clear.
private let internalQueue: OperationQueue = {
let queue = OperationQueue()
queue.name = "WdtWeatherSource Queue"
queue.maxConcurrentOperationCount = 1
return queue
}()
/// This is used
private var locationsBeingUpdated = Set<Location>()
public let weatherUpdateInterval = TimeInterval(15 * 60) // 15 minutes
public func updateWeather(for location: Location, completion: @escaping WeatherSourceCompletion) {
log.debug("Start update.")
internalQueue.addOperation { [weak self] in
let extendedCompletion: WeatherSourceCompletion = { [weak self] (updatedLocation, error) in
self?.internalQueue.addOperation {
completion(updatedLocation, error)
self?.locationsBeingUpdated.remove(location)
}
}
self?.updateWeatherInternal(for: location, completion: extendedCompletion)
}
}
/// This method should only be run from the internalQueue.
private func updateWeatherInternal(for location: Location, completion: @escaping WeatherSourceCompletion) {
guard !locationsBeingUpdated.contains(location) else {
completion(nil, WdtWeatherSourceError.alreadyBeingUpdated)
return
}
log.debug("Start update for \(location)")
guard var urlComponents = URLComponents(string: WdtWeatherSource.updateUrlMega) else {
assertionFailure("Should never happen. The URL should be correct.")
return
......@@ -42,6 +73,11 @@ public class WdtWeatherSource: WeatherSource {
queryParameters["STATE"] = location.region
queryParameters["COUNTRY"] = location.countryName
}
guard !queryParameters.isEmpty else {
completion(nil, WdtWeatherSourceError.insufficientLocationInfo)
log.error("Not enough information about location.")
return
}
queryParameters["UNITS"] = "all"
urlComponents.queryItems = queryParameters.map { URLQueryItem(name: $0, value: $1) }
......@@ -49,6 +85,7 @@ public class WdtWeatherSource: WeatherSource {
completion(nil, WdtWeatherSourceError.badUrl)
return
}
log.debug("query params: \(queryParameters)")
let urlSession = URLSession.shared
let dataTask = urlSession.dataTask(with: url) { [weak self] (data, reponse, error) in
......@@ -60,7 +97,7 @@ public class WdtWeatherSource: WeatherSource {
let decoder = XMLDecoder()
do {
let locationResponse = try decoder.decode(WdtLocationResponse.self, from: data)
guard let newLocation = try self.update(location: location, from: locationResponse) else {
guard let newLocation = try self.applyChangesTo(location: location, from: locationResponse) else {
completion(nil, WdtWeatherSourceError.badServerResponse(error))
return
}
......@@ -74,7 +111,7 @@ public class WdtWeatherSource: WeatherSource {
dataTask.resume()
}
func update(location: Location, from locationResponse: WdtLocationResponse) throws -> Location? {
private func applyChangesTo(location: Location, from locationResponse: WdtLocationResponse) throws -> Location? {
guard let wdtLocation = locationResponse.locations.first else {
return nil
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment