Commit ca3c2cd5 by Demid Merzlyakov

Health: added model objects.

parent 45c821a5
...@@ -117,6 +117,10 @@ ...@@ -117,6 +117,10 @@
CDE2BF252609D9140085C930 /* ForecastWindButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDE2BF242609D9140085C930 /* ForecastWindButton.swift */; }; CDE2BF252609D9140085C930 /* ForecastWindButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDE2BF242609D9140085C930 /* ForecastWindButton.swift */; };
CDEE8AD725DA882200C289DE /* ForecastPeriodButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDEE8AD625DA882200C289DE /* ForecastPeriodButton.swift */; }; CDEE8AD725DA882200C289DE /* ForecastPeriodButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDEE8AD625DA882200C289DE /* ForecastPeriodButton.swift */; };
CDF9BF8E26133D050037847D /* LocationSearchCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDF9BF8D26133D050037847D /* LocationSearchCoordinator.swift */; }; CDF9BF8E26133D050037847D /* LocationSearchCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDF9BF8D26133D050037847D /* LocationSearchCoordinator.swift */; };
CE28474F26159857006C8DC5 /* HealthSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE28474E26159857006C8DC5 /* HealthSource.swift */; };
CE28475226159A32006C8DC5 /* BlendHealthCenterModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE28475126159A32006C8DC5 /* BlendHealthCenterModels.swift */; };
CE28475D2615A5B3006C8DC5 /* Health.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE28475C2615A5B3006C8DC5 /* Health.swift */; };
CE2847602615A8AD006C8DC5 /* BlendHealthSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE28475F2615A8AD006C8DC5 /* BlendHealthSource.swift */; };
CE578FD325F7E89400E8B85D /* DayTimeWeather.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE578FD225F7E89400E8B85D /* DayTimeWeather.swift */; }; CE578FD325F7E89400E8B85D /* DayTimeWeather.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE578FD225F7E89400E8B85D /* DayTimeWeather.swift */; };
CE578FE525FB415F00E8B85D /* CityCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE578FE225FB415F00E8B85D /* CityCell.swift */; }; CE578FE525FB415F00E8B85D /* CityCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE578FE225FB415F00E8B85D /* CityCell.swift */; };
CE578FE625FB415F00E8B85D /* LocationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE578FE325FB415F00E8B85D /* LocationViewController.swift */; }; CE578FE625FB415F00E8B85D /* LocationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE578FE325FB415F00E8B85D /* LocationViewController.swift */; };
...@@ -269,6 +273,10 @@ ...@@ -269,6 +273,10 @@
CDE2BF242609D9140085C930 /* ForecastWindButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ForecastWindButton.swift; sourceTree = "<group>"; }; CDE2BF242609D9140085C930 /* ForecastWindButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ForecastWindButton.swift; sourceTree = "<group>"; };
CDEE8AD625DA882200C289DE /* ForecastPeriodButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ForecastPeriodButton.swift; sourceTree = "<group>"; }; CDEE8AD625DA882200C289DE /* ForecastPeriodButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ForecastPeriodButton.swift; sourceTree = "<group>"; };
CDF9BF8D26133D050037847D /* LocationSearchCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationSearchCoordinator.swift; sourceTree = "<group>"; }; CDF9BF8D26133D050037847D /* LocationSearchCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationSearchCoordinator.swift; sourceTree = "<group>"; };
CE28474E26159857006C8DC5 /* HealthSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HealthSource.swift; sourceTree = "<group>"; };
CE28475126159A32006C8DC5 /* BlendHealthCenterModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlendHealthCenterModels.swift; sourceTree = "<group>"; };
CE28475C2615A5B3006C8DC5 /* Health.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Health.swift; sourceTree = "<group>"; };
CE28475F2615A8AD006C8DC5 /* BlendHealthSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlendHealthSource.swift; sourceTree = "<group>"; };
CE578FD225F7E89400E8B85D /* DayTimeWeather.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DayTimeWeather.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>"; }; 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>"; }; CE578FE325FB415F00E8B85D /* LocationViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LocationViewController.swift; sourceTree = "<group>"; };
...@@ -729,6 +737,43 @@ ...@@ -729,6 +737,43 @@
path = Forecast; path = Forecast;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
CE28474C261597EB006C8DC5 /* Health */ = {
isa = PBXGroup;
children = (
CE28474D261597F1006C8DC5 /* Model */,
CE28474E26159857006C8DC5 /* HealthSource.swift */,
CE28475F2615A8AD006C8DC5 /* BlendHealthSource.swift */,
);
path = Health;
sourceTree = "<group>";
};
CE28474D261597F1006C8DC5 /* Model */ = {
isa = PBXGroup;
children = (
CE28475126159A32006C8DC5 /* BlendHealthCenterModels.swift */,
);
path = Model;
sourceTree = "<group>";
};
CE28475726159B78006C8DC5 /* Weather */ = {
isa = PBXGroup;
children = (
CEAFF08825DFC6B200DF4EBF /* CurrentWeather.swift */,
CEAFF08B25DFC6BC00DF4EBF /* DailyWeather.swift */,
CEAFF08E25DFC6ED00DF4EBF /* HourlyWeather.swift */,
CE578FD225F7E89400E8B85D /* DayTimeWeather.swift */,
);
path = Weather;
sourceTree = "<group>";
};
CE28475926159B85006C8DC5 /* Health */ = {
isa = PBXGroup;
children = (
CE28475C2615A5B3006C8DC5 /* Health.swift */,
);
path = Health;
sourceTree = "<group>";
};
CE578FE025FB415E00E8B85D /* Locations */ = { CE578FE025FB415E00E8B85D /* Locations */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
...@@ -775,6 +820,7 @@ ...@@ -775,6 +820,7 @@
CEAFF09925DFC78200DF4EBF /* Network */ = { CEAFF09925DFC78200DF4EBF /* Network */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
CE28474C261597EB006C8DC5 /* Health */,
87C1724825FF94F400DA3464 /* ConfigManager.swift */, 87C1724825FF94F400DA3464 /* ConfigManager.swift */,
87C171F325FF7A4000DA3464 /* PopularCitiesManager.swift */, 87C171F325FF7A4000DA3464 /* PopularCitiesManager.swift */,
87C171F125FF7A3300DA3464 /* Weather */, 87C171F125FF7A3300DA3464 /* Weather */,
...@@ -799,10 +845,8 @@ ...@@ -799,10 +845,8 @@
children = ( children = (
87C1720C25FF870600DA3464 /* GeoNamesPlace.swift */, 87C1720C25FF870600DA3464 /* GeoNamesPlace.swift */,
CEAFF08225DFC67F00DF4EBF /* Location.swift */, CEAFF08225DFC67F00DF4EBF /* Location.swift */,
CEAFF08825DFC6B200DF4EBF /* CurrentWeather.swift */, CE28475726159B78006C8DC5 /* Weather */,
CEAFF08B25DFC6BC00DF4EBF /* DailyWeather.swift */, CE28475926159B85006C8DC5 /* Health */,
CEAFF08E25DFC6ED00DF4EBF /* HourlyWeather.swift */,
CE578FD225F7E89400E8B85D /* DayTimeWeather.swift */,
); );
path = ModelObjects; path = ModelObjects;
sourceTree = "<group>"; sourceTree = "<group>";
...@@ -1014,10 +1058,13 @@ ...@@ -1014,10 +1058,13 @@
CEAFF08925DFC6B200DF4EBF /* CurrentWeather.swift in Sources */, CEAFF08925DFC6B200DF4EBF /* CurrentWeather.swift in Sources */,
CD866A72260F6A5300E96A5C /* SettingsDetailsCell.swift in Sources */, CD866A72260F6A5300E96A5C /* SettingsDetailsCell.swift in Sources */,
CEC5270325E7BB4000DA58A5 /* WdtSurfaceObservation.swift in Sources */, CEC5270325E7BB4000DA58A5 /* WdtSurfaceObservation.swift in Sources */,
CE28474F26159857006C8DC5 /* HealthSource.swift in Sources */,
CEAFF08C25DFC6BD00DF4EBF /* DailyWeather.swift in Sources */, CEAFF08C25DFC6BD00DF4EBF /* DailyWeather.swift in Sources */,
CEDE4F0B25EFA3A7007457E9 /* UpdatableModelObject.swift in Sources */, CEDE4F0B25EFA3A7007457E9 /* UpdatableModelObject.swift in Sources */,
CE28475226159A32006C8DC5 /* BlendHealthCenterModels.swift in Sources */,
87C171EE25FF79CC00DA3464 /* AdConfigManager.swift in Sources */, 87C171EE25FF79CC00DA3464 /* AdConfigManager.swift in Sources */,
CDD0F1E82572429E00CF5017 /* AppFont.swift in Sources */, CDD0F1E82572429E00CF5017 /* AppFont.swift in Sources */,
CE28475D2615A5B3006C8DC5 /* Health.swift in Sources */,
CEF9599F2601DF3300975FAA /* AdLogger.swift in Sources */, CEF9599F2601DF3300975FAA /* AdLogger.swift in Sources */,
CDC6124F25E7964700188DA7 /* TodayDayTimesCell.swift in Sources */, CDC6124F25E7964700188DA7 /* TodayDayTimesCell.swift in Sources */,
CD593BC226088A5900C93428 /* TimePeriodOffsetHolder.swift in Sources */, CD593BC226088A5900C93428 /* TimePeriodOffsetHolder.swift in Sources */,
...@@ -1048,6 +1095,7 @@ ...@@ -1048,6 +1095,7 @@
CD17C60225D15C8500EE884E /* CoordinatorProtocol.swift in Sources */, CD17C60225D15C8500EE884E /* CoordinatorProtocol.swift in Sources */,
CDA5542825EF734200A2E08C /* TodayCellFactory.swift in Sources */, CDA5542825EF734200A2E08C /* TodayCellFactory.swift in Sources */,
CEF959742600C3A400975FAA /* FlurryAnalyticsService.swift in Sources */, CEF959742600C3A400975FAA /* FlurryAnalyticsService.swift in Sources */,
CE2847602615A8AD006C8DC5 /* BlendHealthSource.swift in Sources */,
CD86C22225F0DCCB00F38A16 /* PrecipitationView.swift in Sources */, CD86C22225F0DCCB00F38A16 /* PrecipitationView.swift in Sources */,
CD17C5FF25D15B7C00EE884E /* TodayCoordinator.swift in Sources */, CD17C5FF25D15B7C00EE884E /* TodayCoordinator.swift in Sources */,
CD822FF525D6817000A05501 /* TodayForecastCell.swift in Sources */, CD822FF525D6817000A05501 /* TodayForecastCell.swift in Sources */,
......
...@@ -32,4 +32,25 @@ extension UIColor { ...@@ -32,4 +32,25 @@ extension UIColor {
} }
self.init(red: CGFloat(r) / 255, green: CGFloat(g) / 255, blue: CGFloat(b) / 255, alpha: CGFloat(255 * alpha) / 255) self.init(red: CGFloat(r) / 255, green: CGFloat(g) / 255, blue: CGFloat(b) / 255, alpha: CGFloat(255 * alpha) / 255)
} }
public func toHex(alpha: Bool = false) -> String? {
guard let components = cgColor.components, components.count >= 3 else {
return nil
}
let r = Float(components[0])
let g = Float(components[1])
let b = Float(components[2])
var a = Float(1.0)
if components.count >= 4 {
a = Float(components[3])
}
if alpha {
return String(format: "%02lX%02lX%02lX%02lX", lroundf(r * 255), lroundf(g * 255), lroundf(b * 255), lroundf(a * 255))
} else {
return String(format: "%02lX%02lX%02lX", lroundf(r * 255), lroundf(g * 255), lroundf(b * 255))
}
}
} }
//
// Health.swift
// 1Weather
//
// Created by Demid Merzlyakov on 01.04.2021.
//
import Foundation
import UIKit
public struct Health: Equatable, Hashable {
public let lastUpdateTime: Date
public let airQuality: AirQuality?
public let pollutants: [String: Pollutant]
}
public struct AirQuality: Equatable, Hashable {
public let index: Double
public let airQualityDescription: String? // long name to differentiate from Swift's .description
public let color: UIColor
}
public struct Pollutant: Equatable, Hashable {
public let name: String
public let value: Double
public let color: UIColor
public let status: String
}
...@@ -59,6 +59,7 @@ public struct Location { ...@@ -59,6 +59,7 @@ public struct Location {
} }
} }
public private (set) var dayTimeForecast: [DayTimeWeather] = [DayTimeWeather]() public private (set) var dayTimeForecast: [DayTimeWeather] = [DayTimeWeather]()
public var health: Health?
// MARK: - Derived fields // MARK: - Derived fields
public var cityId: String { public var cityId: String {
...@@ -118,6 +119,7 @@ extension Location: UpdatableModelObject { ...@@ -118,6 +119,7 @@ extension Location: UpdatableModelObject {
result.today = result.today?.mergedWith(incrementalChanges: incrementalChanges.today) ?? incrementalChanges.today result.today = result.today?.mergedWith(incrementalChanges: incrementalChanges.today) ?? incrementalChanges.today
result.daily = result.daily.mergedWith(incrementalChanges: incrementalChanges.daily) result.daily = result.daily.mergedWith(incrementalChanges: incrementalChanges.daily)
result.hourly = result.hourly.mergedWith(incrementalChanges: incrementalChanges.hourly) result.hourly = result.hourly.mergedWith(incrementalChanges: incrementalChanges.hourly)
result.health = incrementalChanges.health ?? result.health
return result return result
} }
} }
......
//
// BlendHealthSource.swift
// 1Weather
//
// Created by Demid Merzlyakov on 01.04.2021.
//
import Foundation
public enum BlendHealthError: Error {
case insufficientLocationInfo
case badUrl
case networkError(Error?)
case badServerResponse(Error?)
case dataEncodingError(String)
case alreadyBeingUpdated
}
public class BlendHealthSource {
}
//
// HealthSource.swift
// 1Weather
//
// Created by Demid Merzlyakov on 01.04.2021.
//
import Foundation
public typealias HealthSourceCompletion = (Health?, Error?) -> ()
public protocol HealthSource {
var healthUpdateInterval: TimeInterval { get }
func updateHelath(for location: Location, completion: @escaping HealthSourceCompletion)
}
//
// BlendHealthCenterModels.swift
// 1Weather
//
// Created by Demid Merzlyakov on 01.04.2021.
//
import Foundation
import UIKit
// MARK: - HealthCenter
struct BlendHealthCenter: Codable {
public let s2CellID: String
public let updatedOn: Date
public let airQuality: BlendAirQuality
public let fire: BlendFire
public let pollutants, pollen: [BlendPoll]
enum CodingKeys: String, CodingKey {
case s2CellID = "s2_cell_id"
case updatedOn = "updated_on"
case airQuality = "air_quality"
case fire, pollutants, pollen
}
}
// MARK: - AirQuality
struct BlendAirQuality: Codable {
public let aqi: Double
public let airQualityDescription: String
public let healthAdvice: BlendHealthAdvice
public let color: BlendColor
public let imageURL: String
enum CodingKeys: String, CodingKey {
case aqi
case airQualityDescription = "description"
case healthAdvice = "health_advice"
case color = "color_code"
case imageURL = "image_url"
}
}
// MARK: - HealthAdvice
struct BlendHealthAdvice: Codable {
public let general, sensitive, active: String
}
// MARK: - Fire
struct BlendFire: Codable {
public let windSpeed: Double
public let windDirection, siUnit, fireDescription: String
enum CodingKeys: String, CodingKey {
case windSpeed = "wind_speed"
case windDirection = "wind_direction"
case siUnit = "si_unit"
case fireDescription = "description"
}
}
// MARK: - Poll
struct BlendPoll: Codable {
public let name: String
public let value: Double?
public let siUnit: String
public let status, colorCode: String?
enum CodingKeys: String, CodingKey {
case name, value
case siUnit = "si_unit"
case status
case colorCode = "color_code"
}
}
struct BlendColor: Codable {
public let uiColor: UIColor
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
let hexString = try container.decode(String.self)
self.uiColor = UIColor(hexString: hexString)
}
func encode(to encoder: Encoder) throws {
guard let hexRepresentation = uiColor.toHex() else {
throw BlendHealthError.dataEncodingError("BlendColor to HEX conversion failed for color \(uiColor)")
}
var container = encoder.singleValueContainer()
try container.encode("#\(hexRepresentation)")
}
}
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