Commit e1ae935a by Demid Merzlyakov

Alerts parsing is working.

parent e45681f9
......@@ -72,7 +72,7 @@
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Debug"
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
......@@ -166,6 +166,8 @@ public class LocationManager {
self.storage = storage
self.deviceLocationMonitor.delegate = self
self.nwsAlertsManager.delegate = self
storage.load { [weak self] (locations, selectedIndex, error) in
DispatchQueue.main.async {
guard let self = self else { return }
......@@ -473,3 +475,14 @@ extension LocationManager: DeviceLocationMonitorDelegate {
}
}
}
// MARK: - NWSAlertsManagerDelegate
extension LocationManager: NWSAlertsManagerDelegate {
public func extendedInfoAvailable(from alertsManager: NWSAlertsManager, for alert: NWSAlert) {
DispatchQueue.main.async {
if let alertIndex = self.selectedLocation?.notifications?.nwsAlerts.firstIndex(of: alert) {
self.selectedLocation?.notifications?.nwsAlerts[alertIndex] = alert
}
}
}
}
......@@ -9,5 +9,5 @@ import Foundation
public struct Notifications {
public let updatedAt: Date
public let nwsAlerts: [NWSAlert]
public var nwsAlerts: [NWSAlert]
}
......@@ -8,7 +8,6 @@
import Foundation
public struct NWSAlert: Codable, Equatable, Hashable {
public var issueDate: Date?
public let weatherID: String
public let messageID: String
public let messageURL: String
......
......@@ -18,8 +18,7 @@ class NWSAlertInfoParser {
}
let urlSession = URLSession.shared
urlSession.dataTask(with: url) { [weak self] (data, response, error) in
guard let self = self else { return }
urlSession.dataTask(with: url) { (data, response, error) in
if let httpResponse = response as? HTTPURLResponse {
guard httpResponse.statusCode >= 200 && httpResponse.statusCode < 400 else {
log.error("HTTP error \(httpResponse.statusCode)")
......@@ -32,9 +31,16 @@ class NWSAlertInfoParser {
completion(nil)
return
}
log.debug("Response: \(responseString)")
let extendedInfo: NWSAlertExtendedInfo = self.parseExtendedInfo(from: responseString)
completion(extendedInfo)
let debugEncoder = JSONEncoder()
debugEncoder.outputFormatting = .prettyPrinted
debugEncoder.dateEncodingStrategy = .iso8601
if let jsonData = try? debugEncoder.encode(extendedInfo), let json = String(data: jsonData, encoding: .utf8) {
log.debug("Parsed: \(json)")
}
completion(extendedInfo)
}.resume()
}
// MARK: - Private methods
......
......@@ -15,7 +15,12 @@ public enum NWSError: Error {
case badServerResponse(Error?)
}
public protocol NWSAlertsManagerDelegate: class {
func extendedInfoAvailable(from alertsManager: NWSAlertsManager, for alert: NWSAlert)
}
public class NWSAlertsManager {
public weak var delegate: NWSAlertsManagerDelegate?
public typealias Completion = ([NWSAlert]?, NWSError?) -> ()
#if DEBUG
public let updateInterval = TimeInterval(2 * 60) // 2 minutes
......@@ -33,11 +38,12 @@ public class NWSAlertsManager {
private let log = Logger(componentName: "NWSAlertsManager")
private var locationsBeingUpdated = Set<Location>()
private var extendedInfoBeingFetched = Set<NWSAlert>()
private var baseUrl: String {
useStaging ? baseUrlStaging : baseUrlProduction
}
/// This queue is needed to synchronize access to locationsBeingUpdated and the alerts list. Also, to make logging more clear.
/// This queue is needed to synchronize access to locationsBeingUpdated and extendedInfoBeingFetched. Also, to make logging more clear.
private let internalQueue: OperationQueue = {
let queue = OperationQueue()
queue.name = "WdtWeatherSource Queue"
......@@ -66,6 +72,10 @@ public class NWSAlertsManager {
}
}
public func extendedInfo(for alert: NWSAlert) -> NWSAlertExtendedInfo? {
return alerts.first(where: { $0 == alert })?.extendedInfo
}
private func fetchActiveAlertsInternal(for location: Location, completion: @escaping NWSAlertsManager.Completion) {
guard !locationsBeingUpdated.contains(location) else {
completion(nil, .alreadyBeingUpdated)
......@@ -133,6 +143,7 @@ public class NWSAlertsManager {
self.merge(alerts: eventsWithLocation)
completion(eventsWithLocation, nil)
}
catch {
completion(nil, .badServerResponse(error))
......@@ -141,7 +152,35 @@ public class NWSAlertsManager {
dataTask.resume()
}
func merge(alerts newAlerts: [NWSAlert]) {
private func fetchAllExtendedInfo(for alerts: [NWSAlert]) {
internalQueue.addOperation { [weak self] in
guard let self = self else { return }
var newAlertsToFetch = [NWSAlert]()
let parser = NWSAlertInfoParser()
for alert in alerts {
if !self.extendedInfoBeingFetched.contains(alert) {
self.extendedInfoBeingFetched.insert(alert)
parser.fetchExtendedInfo(for: alert) { [weak self] (extendedInfo) in
DispatchQueue.main.async { [weak self] in
guard let self = self else { return }
guard let alertIndex = self.alerts.firstIndex(where: { $0 == alert }) else {
return
}
self.alerts[alertIndex].extendedInfo = extendedInfo
self.internalQueue.addOperation {
self.extendedInfoBeingFetched.remove(alert)
}
self.delegate?.extendedInfoAvailable(from: self, for: self.alerts[alertIndex])
}
}
}
}
}
}
private func merge(alerts newAlerts: [NWSAlert]) {
//TODO: optimize
var resultingSet = Set<NWSAlert>()
for alert in self.alerts {
......@@ -150,7 +189,7 @@ public class NWSAlertsManager {
}
}
for alert in newAlerts {
if !alert.expired {
if !alert.expired && !resultingSet.contains(alert) {
resultingSet.insert(alert)
}
}
......@@ -163,5 +202,6 @@ public class NWSAlertsManager {
DispatchQueue.main.sync {
self.alerts = resultingAlerts
}
fetchAllExtendedInfo(for: resultingAlerts)
}
}
......@@ -83,7 +83,7 @@ class TodayAlertCell: UITableViewCell {
infoLabel.text = infoTemplate
if let issueDate = mostSevereAlert.issueDate {
if let extendedInfo = mostSevereAlert.extendedInfo, let issueDate = extendedInfo.issueDate {
let secondsAgo = Date().timeIntervalSince(issueDate)
let formatter = DateComponentsFormatter()
formatter.allowedUnits = [.day, .hour, .minute, .second]
......
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