Commit e5083f2c by Demid Merzlyakov

Fetch FIPS codes & set FIPS_LIST.

parent 5719c7d2
......@@ -199,6 +199,8 @@
CE308B2A2637EA8E001ECD8A /* _CoreNWSAlert.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE308B282637EA8E001ECD8A /* _CoreNWSAlert.swift */; };
CE308B2B2637EA8E001ECD8A /* _CoreNotifications.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE308B292637EA8E001ECD8A /* _CoreNotifications.swift */; };
CE376C98261EE484000B1159 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = CE376C97261EE484000B1159 /* LaunchScreen.storyboard */; };
CE3A36C72638A77E002CACC3 /* BlendFIPSSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE3A36C62638A77E002CACC3 /* BlendFIPSSource.swift */; };
CE3A36ED2638A825002CACC3 /* FIPSResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE3A36EC2638A825002CACC3 /* FIPSResponse.swift */; };
CE578FD325F7E89400E8B85D /* DayTimeWeather.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE578FD225F7E89400E8B85D /* DayTimeWeather.swift */; };
CE578FE525FB415F00E8B85D /* CityCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE578FE225FB415F00E8B85D /* CityCell.swift */; };
CE578FE625FB415F00E8B85D /* LocationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE578FE325FB415F00E8B85D /* LocationViewController.swift */; };
......@@ -236,6 +238,7 @@
CEAFF0A325E0FF0800DF4EBF /* LocationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEAFF0A225E0FF0800DF4EBF /* LocationManager.swift */; };
CEBAC1C62638236D00A89681 /* PushNotificationsManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEBAC1C52638236D00A89681 /* PushNotificationsManager.swift */; };
CEBAC1C82638240800A89681 /* DeeplinksRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEBAC1C72638240800A89681 /* DeeplinksRouter.swift */; };
CEBAC2122638968D00A89681 /* FIPSSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEBAC2112638968D00A89681 /* FIPSSource.swift */; };
CEC526FA25E7959A00DA58A5 /* WeatherSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEC526F925E7959A00DA58A5 /* WeatherSource.swift */; };
CEC526FD25E795F700DA58A5 /* WdtWeatherSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEC526FC25E795F700DA58A5 /* WdtWeatherSource.swift */; };
CEC5270025E7BACB00DA58A5 /* WdtLocation.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEC526FF25E7BACB00DA58A5 /* WdtLocation.swift */; };
......@@ -484,6 +487,8 @@
CE308B282637EA8E001ECD8A /* _CoreNWSAlert.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = _CoreNWSAlert.swift; sourceTree = "<group>"; };
CE308B292637EA8E001ECD8A /* _CoreNotifications.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = _CoreNotifications.swift; sourceTree = "<group>"; };
CE376C97261EE484000B1159 /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = LaunchScreen.storyboard; sourceTree = "<group>"; };
CE3A36C62638A77E002CACC3 /* BlendFIPSSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlendFIPSSource.swift; sourceTree = "<group>"; };
CE3A36EC2638A825002CACC3 /* FIPSResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FIPSResponse.swift; sourceTree = "<group>"; };
CE578FD225F7E89400E8B85D /* DayTimeWeather.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DayTimeWeather.swift; sourceTree = "<group>"; };
CE578FE225FB415F00E8B85D /* CityCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CityCell.swift; sourceTree = "<group>"; };
CE578FE325FB415F00E8B85D /* LocationViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LocationViewController.swift; sourceTree = "<group>"; };
......@@ -521,6 +526,7 @@
CEAFF0A225E0FF0800DF4EBF /* LocationManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationManager.swift; sourceTree = "<group>"; };
CEBAC1C52638236D00A89681 /* PushNotificationsManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PushNotificationsManager.swift; sourceTree = "<group>"; };
CEBAC1C72638240800A89681 /* DeeplinksRouter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeeplinksRouter.swift; sourceTree = "<group>"; };
CEBAC2112638968D00A89681 /* FIPSSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FIPSSource.swift; sourceTree = "<group>"; };
CEC526F925E7959A00DA58A5 /* WeatherSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeatherSource.swift; sourceTree = "<group>"; };
CEC526FC25E795F700DA58A5 /* WdtWeatherSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WdtWeatherSource.swift; sourceTree = "<group>"; };
CEC526FF25E7BACB00DA58A5 /* WdtLocation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WdtLocation.swift; sourceTree = "<group>"; };
......@@ -1227,6 +1233,8 @@
CE13B98026272A13007CBD4D /* Model */,
CE0456232629C04C003D252B /* NWSAlertsManager.swift */,
CEE0A1A326317A8F0044C257 /* NWSAlertInfoParser.swift */,
CEBAC2112638968D00A89681 /* FIPSSource.swift */,
CE3A36C62638A77E002CACC3 /* BlendFIPSSource.swift */,
);
path = Notifications;
sourceTree = "<group>";
......@@ -1239,6 +1247,7 @@
CEE0A1A126317A3F0044C257 /* NWSSeverityLevel.swift */,
CEE0A19F26317A1E0044C257 /* NWSAlertExtendedInfo.swift */,
CEE0A17A263179E50044C257 /* NWSAlertInfoBlock.swift */,
CE3A36EC2638A825002CACC3 /* FIPSResponse.swift */,
);
path = Model;
sourceTree = "<group>";
......@@ -1737,6 +1746,7 @@
CD67617C2625A60B0079D273 /* MapLayersDismissAnimator.swift in Sources */,
CEAFF08C25DFC6BD00DF4EBF /* DailyWeather.swift in Sources */,
CEDE4F0B25EFA3A7007457E9 /* UpdatableModelObject.swift in Sources */,
CE3A36C72638A77E002CACC3 /* BlendFIPSSource.swift in Sources */,
CE13B814262480B3007CBD4D /* BRNativeBannerView.swift in Sources */,
CE13B7E126247BF9007CBD4D /* UserDefaultsOptionalValue.swift in Sources */,
CE8962A626175DF500CA274A /* _CoreHealth.swift in Sources */,
......@@ -1839,6 +1849,7 @@
CE9F01C1261B3776009BA500 /* CoreDataUtils.swift in Sources */,
CD67616D262587D30079D273 /* UITabBarController+Hide.swift in Sources */,
CDC6126225E8DAB800188DA7 /* MoonPhaseCell.swift in Sources */,
CEBAC2122638968D00A89681 /* FIPSSource.swift in Sources */,
CD37D3D6260C93B3002669D6 /* MenuCellFactory.swift in Sources */,
CD8B60AD263819400055CB3F /* NWSAlertInfoBlockTableViewCell.swift in Sources */,
87D815AC2636D61D0015A6D1 /* NWSAlertViewModel.swift in Sources */,
......@@ -1877,6 +1888,7 @@
CD32CE0B260C744A00235081 /* MenuCoordinator.swift in Sources */,
CD55E0BB2615EE2400CC4DC7 /* PollutantView.swift in Sources */,
CE308B2A2637EA8E001ECD8A /* _CoreNWSAlert.swift in Sources */,
CE3A36ED2638A825002CACC3 /* FIPSResponse.swift in Sources */,
CD8B60B3263819790055CB3F /* NWSAlertCell.swift in Sources */,
CE13B81C262480B3007CBD4D /* Interstitial.swift in Sources */,
CDDE8D7C262EED3C00267931 /* MapLegendSevereView.swift in Sources */,
......
......@@ -21,6 +21,7 @@ public class LocationManager {
private let weatherUpdateSource: WeatherSource
private let healthSource: HealthSource
private let fipsSource: FIPSSource
public let nwsAlertsManager: NWSAlertsManager
private let storage: Storage
private var defaultLocation = Location(deviceLocation: false,
......@@ -154,15 +155,17 @@ public class LocationManager {
weatherUpdateSource: WdtWeatherSource(),
healthSource: BlendHealthSource(),
nwsAlertsManager: NWSAlertsManager(),
fipsSource: BlendFIPSSource(),
storage: DelayedSaveStorage(storage: CoreDataStorage(), delay: 2))
public let maxLocationsCount = 12
public init(weatherUpdateSource: WeatherSource, healthSource: HealthSource, nwsAlertsManager: NWSAlertsManager, storage: Storage) {
public init(weatherUpdateSource: WeatherSource, healthSource: HealthSource, nwsAlertsManager: NWSAlertsManager, fipsSource: FIPSSource, storage: Storage) {
self.weatherUpdateSource = weatherUpdateSource
self.healthSource = healthSource
self.deviceLocationMonitor = DeviceLocationMonitor()
self.nwsAlertsManager = nwsAlertsManager
self.fipsSource = fipsSource
self.storage = storage
self.deviceLocationMonitor.delegate = self
......@@ -204,6 +207,21 @@ public class LocationManager {
}
updateHealth(for: location)
updateNotifications(for: location)
getFipsIfNeeded(for: location)
}
}
public func getFipsIfNeeded(for location: Location) {
if location.fipsCode == nil {
fipsSource.getFipsCode(for: location) { [weak self] (fipsCode) in
if let fipsCode = fipsCode {
self?.makeChanges(to: location, in: "getFips") { (location) -> Location in
var updatedLocation = location
updatedLocation.fipsCode = fipsCode
return updatedLocation
}
}
}
}
}
......
//
// BlendFIPSSource.swift
// 1Weather
//
// Created by Demid Merzlyakov on 28.04.2021.
//
import Foundation
class BlendFIPSSource: FIPSSource {
private let log = Logger(componentName: "BlendFIPSSource")
private let baseUrlProduction = "https://nwsalert.onelouder.com"
private let baseUrlStaging = "https://sta-nwsalert.onelouder.com"
private static let blendAPIKeyHeaderName = "blend-api-key"
private static let blendAPIKey = "0imfnc8mVLWwsAawjYr4Rx-Af50DDqtlx"
#if DEBUG
public var useStaging = true
#else
public var useStaging = false
#endif
private var baseUrl: String {
useStaging ? baseUrlStaging : baseUrlProduction
}
/// This queue is needed to synchronize access to locationsBeingUpdated. Also, to make logging more clear.
private let internalQueue: OperationQueue = {
let queue = OperationQueue()
queue.name = "BlendHealthSource Queue"
queue.maxConcurrentOperationCount = 1
return queue
}()
private var locationsBeingUpdated = Set<Location>()
public func getFipsCode(for location: Location, completion: @escaping FIPSSourceeCompletion) {
internalQueue.addOperation { [weak self] in
let extendedCompletion: FIPSSourceeCompletion = { [weak self] (fipsCode) in
self?.internalQueue.addOperation {
completion(fipsCode)
self?.locationsBeingUpdated.remove(location)
}
}
self?.getFipsCodeInternal(for: location, completion: extendedCompletion)
}
}
private func getFipsCodeInternal(for location: Location, completion: @escaping FIPSSourceeCompletion) {
guard !locationsBeingUpdated.contains(location) else {
completion(nil)
return
}
locationsBeingUpdated.insert(location)
guard let url = URL(string: self.baseUrl + "/1weather/api/v1/location") else {
assertionFailure("Should never happen. The URL should be correct.")
return
}
var queryParameters = [String: String]()
if let coordinates = location.coordinates {
queryParameters["lat"] = String(format: "%.5f", coordinates.latitude)
queryParameters["lon"] = String(format: "%.5f", coordinates.longitude)
}
queryParameters["zip"] = location.zip
queryParameters["city"] = location.cityName
queryParameters["state"] = location.region
queryParameters["country"] = location.countryCode
guard !queryParameters.isEmpty else {
completion(nil)
log.error("Not enough information about location.")
return
}
log.debug("Network request (\(location)): \(url)")
var request = URLRequest(url: url)
let encoder = JSONEncoder()
guard let requestBody = try? encoder.encode(queryParameters) else {
completion(nil)
log.error("Couldn't encode request body: \(queryParameters)")
return
}
request.httpBody = requestBody
var headers = request.allHTTPHeaderFields ?? [String: String]()
headers[BlendFIPSSource.blendAPIKeyHeaderName] = BlendFIPSSource.blendAPIKey
headers["Content-Type"] = "application/json"
request.allHTTPHeaderFields = headers
request.httpMethod = "POST"
let urlSession = URLSession.shared
let dataTask = urlSession.dataTask(with: request) { (data, reponse, error) in
// TODO: check response HTTP code
guard let data = data else {
self.log.debug("Network response (\(location)): error \(String(describing: error))")
completion(nil)
return
}
let responseBodyString = String(data: data, encoding: .utf8) ?? "<couldn't show as string"
self.log.debug("Network response (\(location)): \(responseBodyString)")
completion(FIPSResponse(data: data)?.fipsCode)
}
dataTask.resume()
}
}
//
// FIPSSource.swift
// 1Weather
//
// Created by Demid Merzlyakov on 27.04.2021.
//
import Foundation
public typealias FIPSSourceeCompletion = (String?) -> ()
public protocol FIPSSource {
func getFipsCode(for location: Location, completion: @escaping FIPSSourceeCompletion)
}
//
// FIPSResponse.swift
// 1Weather
//
// Created by Demid Merzlyakov on 28.04.2021.
//
import Foundation
struct FIPSResponse: Codable {
let fipsCode, s2CellID: String
enum CodingKeys: String, CodingKey {
case fipsCode = "fips_code"
case s2CellID = "s2_cell_id"
}
}
// MARK: Convenience initializers
extension FIPSResponse {
init?(data: Data) {
guard let me = try? JSONDecoder().decode(FIPSResponse.self, from: data) else { return nil }
self = me
}
init?(_ json: String, using encoding: String.Encoding = .utf8) {
guard let data = json.data(using: encoding) else { return nil }
self.init(data: data)
}
init?(fromURL url: String) {
guard let url = URL(string: url) else { return nil }
guard let data = try? Data(contentsOf: url) else { return nil }
self.init(data: data)
}
var jsonData: Data? {
return try? JSONEncoder().encode(self)
}
var json: String? {
guard let data = self.jsonData else { return nil }
return String(data: data, encoding: .utf8)
}
}
......@@ -41,6 +41,11 @@ class PushNotificationsManager: NSObject {
}
}
public func updateNwsSubscriptions(for locations: [Location]) {
let fipsCodes = locations.compactMap { $0.fipsCode }
MoEngage.sharedInstance().setUserAttribute(fipsCodes.joined(separator: ","), forKey: "FIPS_LIST")
}
public func set(pushToken: Data) {
let tokenString = pushToken.map { String(format: "%02.2hhx", $0) }.joined()
log.info("Got new APNS token: \(tokenString)")
......
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