Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
1
1weather
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Dmitriy Stepanets
1weather
Commits
1f4dd173
Commit
1f4dd173
authored
Apr 02, 2021
by
Demid Merzlyakov
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Health: show actual data.
parent
0677389b
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
73 additions
and
25 deletions
+73
-25
1Weather/Model/LocationManager.swift
+40
-2
1Weather/Network/Health/BlendHealthSource.swift
+21
-9
1Weather/Network/Health/Model/BlendHealthModels.swift
+1
-3
1Weather/UI/View controllers/Today/Cells/TodayAirQualityCell/TodayAirQualityCell.swift
+4
-4
1Weather/UI/View controllers/Today/Cells/TodayCellFactory.swift
+7
-7
No files found.
1Weather/Model/LocationManager.swift
View file @
1f4dd173
...
@@ -20,6 +20,7 @@ public class LocationManager {
...
@@ -20,6 +20,7 @@ public class LocationManager {
private
let
deviceLocationMonitor
:
DeviceLocationMonitor
private
let
deviceLocationMonitor
:
DeviceLocationMonitor
private
let
weatherUpdateSource
:
WeatherSource
private
let
weatherUpdateSource
:
WeatherSource
private
let
healthSource
:
HealthSource
private
var
defaultLocation
=
Location
(
deviceLocation
:
false
,
private
var
defaultLocation
=
Location
(
deviceLocation
:
false
,
coordinates
:
.
init
(
latitude
:
37.3230
,
longitude
:
-
122.0322
),
// Cupertino
coordinates
:
.
init
(
latitude
:
37.3230
,
longitude
:
-
122.0322
),
// Cupertino
timeZone
:
TimeZone
(
abbreviation
:
"PST"
)
!
)
{
timeZone
:
TimeZone
(
abbreviation
:
"PST"
)
!
)
{
...
@@ -124,11 +125,12 @@ public class LocationManager {
...
@@ -124,11 +125,12 @@ public class LocationManager {
}
}
}
}
public
static
let
shared
=
LocationManager
(
weatherUpdateSource
:
WdtWeatherSource
())
public
static
let
shared
=
LocationManager
(
weatherUpdateSource
:
WdtWeatherSource
()
,
healthSource
:
BlendHealthSource
()
)
public
let
maxLocationsCount
=
12
public
let
maxLocationsCount
=
12
public
init
(
weatherUpdateSource
:
WeatherSource
)
{
public
init
(
weatherUpdateSource
:
WeatherSource
,
healthSource
:
HealthSource
)
{
self
.
weatherUpdateSource
=
weatherUpdateSource
self
.
weatherUpdateSource
=
weatherUpdateSource
self
.
healthSource
=
healthSource
self
.
deviceLocationMonitor
=
DeviceLocationMonitor
()
self
.
deviceLocationMonitor
=
DeviceLocationMonitor
()
self
.
deviceLocationMonitor
.
delegate
=
self
self
.
deviceLocationMonitor
.
delegate
=
self
}
}
...
@@ -138,6 +140,7 @@ public class LocationManager {
...
@@ -138,6 +140,7 @@ public class LocationManager {
guard
locations
.
count
>
0
else
{
guard
locations
.
count
>
0
else
{
log
.
info
(
"Update all: update default location if needed."
)
log
.
info
(
"Update all: update default location if needed."
)
updateWeather
(
for
:
defaultLocation
,
updateType
:
.
full
)
updateWeather
(
for
:
defaultLocation
,
updateType
:
.
full
)
updateHealth
(
for
:
defaultLocation
)
return
return
}
}
log
.
info
(
"Update all
\(
locations
.
count
)
locations if needed..."
)
log
.
info
(
"Update all
\(
locations
.
count
)
locations if needed..."
)
...
@@ -148,6 +151,41 @@ public class LocationManager {
...
@@ -148,6 +151,41 @@ public class LocationManager {
if
selectedLocation
!=
location
{
if
selectedLocation
!=
location
{
updateWeather
(
for
:
location
,
updateType
:
.
preferIncremental
)
updateWeather
(
for
:
location
,
updateType
:
.
preferIncremental
)
}
}
updateHealth
(
for
:
location
)
}
}
public
func
updateHealth
(
for
location
:
Location
)
{
if
let
lastTimeUpdated
=
location
.
health
?
.
lastUpdateTime
{
guard
Date
()
.
timeIntervalSince
(
lastTimeUpdated
)
>=
healthSource
.
healthUpdateInterval
else
{
log
.
info
(
"Update health (
\(
location
)
): fresh enough (last updated at
\(
lastTimeUpdated
)
), skip update."
)
return
}
}
log
.
info
(
"Update health for:
\(
location
)
"
)
healthSource
.
updateHelath
(
for
:
location
)
{
[
weak
self
]
(
health
,
error
)
in
guard
let
self
=
self
else
{
return
}
guard
let
health
=
health
else
{
if
let
error
=
error
{
self
.
log
.
error
(
"Update health (
\(
location
)
) error:
\(
error
)
"
)
}
else
{
self
.
log
.
error
(
"Update health (
\(
location
)
) error: unknown error"
)
}
return
// TODO: we need to somehow track failed attempts, so that we didn't request again and again in case of an error (e.g. server is down).
}
DispatchQueue
.
main
.
async
{
if
let
indexToUpdate
=
self
.
locations
.
firstIndex
(
where
:
{
$0
==
location
})
{
self
.
locations
[
indexToUpdate
]
.
health
=
health
}
else
if
self
.
defaultLocation
==
location
{
self
.
defaultLocation
.
health
=
health
}
else
{
self
.
log
.
warning
(
"Update health: Failed to find location after update. Maybe it was deleted while the update was performed. Maybe something went wrong. Location:
\(
location
)
"
)
}
}
}
}
}
}
...
...
1Weather/Network/Health/BlendHealthSource.swift
View file @
1f4dd173
...
@@ -21,7 +21,13 @@ public class BlendHealthSource: HealthSource {
...
@@ -21,7 +21,13 @@ public class BlendHealthSource: HealthSource {
private
let
log
=
Logger
(
componentName
:
"BlendHealthSource"
)
private
let
log
=
Logger
(
componentName
:
"BlendHealthSource"
)
#warning("Not implemented: staging / prod switching!")
#warning("Not implemented: staging / prod switching!")
//TODO: Not implemented: staging / prod switching!
//TODO: Not implemented: staging / prod switching!
private
static
let
healthCenterUrl
=
"http://sta-1w-dataaggregator.onelouder.com/1weather/api/v1/weather/current"
private
static
let
healthCenterUrlStaging
=
"http://sta-1w-dataaggregator.onelouder.com/1weather/api/v1/weather/current"
private
static
let
healthCenterUrlProduction
=
"https://pro-1w-dataaggregator.onelouder.com/1weather/api/v1/weather/current"
private
static
var
healthCenterUrl
:
String
{
return
healthCenterUrlProduction
}
private
static
let
blendAPIKeyHeaderName
=
"blend-api-key"
private
static
let
blendAPIKey
=
"0imfnc8mVLWwsAawjYr4Rx-Af50DDqtlx"
public
var
healthUpdateInterval
:
TimeInterval
=
TimeInterval
(
15
*
60
)
// 15 minutes
public
var
healthUpdateInterval
:
TimeInterval
=
TimeInterval
(
15
*
60
)
// 15 minutes
/// This queue is needed to synchronize access to locationsBeingUpdated. Also, to make logging more clear.
/// This queue is needed to synchronize access to locationsBeingUpdated. Also, to make logging more clear.
...
@@ -60,28 +66,34 @@ public class BlendHealthSource: HealthSource {
...
@@ -60,28 +66,34 @@ public class BlendHealthSource: HealthSource {
var
queryParameters
=
[
String
:
String
]()
var
queryParameters
=
[
String
:
String
]()
if
let
coordinates
=
location
.
coordinates
{
if
let
coordinates
=
location
.
coordinates
{
queryParameters
[
"
LAT
"
]
=
String
(
format
:
"%.5f"
,
coordinates
.
latitude
)
queryParameters
[
"
lat
"
]
=
String
(
format
:
"%.5f"
,
coordinates
.
latitude
)
queryParameters
[
"
LON
"
]
=
String
(
format
:
"%.5f"
,
coordinates
.
longitude
)
queryParameters
[
"
lon
"
]
=
String
(
format
:
"%.5f"
,
coordinates
.
longitude
)
}
}
queryParameters
[
"
ZIP
"
]
=
location
.
zip
queryParameters
[
"
zip
"
]
=
location
.
zip
queryParameters
[
"
CITY
"
]
=
location
.
cityName
queryParameters
[
"
city
"
]
=
location
.
cityName
queryParameters
[
"
STATE
"
]
=
location
.
region
queryParameters
[
"
state
"
]
=
location
.
region
queryParameters
[
"
COUNTRY"
]
=
location
.
countryNam
e
queryParameters
[
"
country"
]
=
location
.
countryCod
e
guard
!
queryParameters
.
isEmpty
else
{
guard
!
queryParameters
.
isEmpty
else
{
completion
(
nil
,
BlendHealthSourceError
.
insufficientLocationInfo
)
completion
(
nil
,
BlendHealthSourceError
.
insufficientLocationInfo
)
log
.
error
(
"Not enough information about location."
)
log
.
error
(
"Not enough information about location."
)
return
return
}
}
urlComponents
.
queryItems
=
queryParameters
.
map
{
URLQueryItem
(
name
:
$0
,
value
:
$1
)
}
guard
let
url
=
urlComponents
.
url
else
{
guard
let
url
=
urlComponents
.
url
else
{
completion
(
nil
,
BlendHealthSourceError
.
badUrl
)
completion
(
nil
,
BlendHealthSourceError
.
badUrl
)
return
return
}
}
log
.
debug
(
"query params:
\(
queryParameters
)
"
)
log
.
debug
(
"query params:
\(
queryParameters
)
"
)
var
request
=
URLRequest
(
url
:
url
)
var
headers
=
request
.
allHTTPHeaderFields
??
[
String
:
String
]()
headers
[
BlendHealthSource
.
blendAPIKeyHeaderName
]
=
BlendHealthSource
.
blendAPIKey
request
.
allHTTPHeaderFields
=
headers
let
urlSession
=
URLSession
.
shared
let
urlSession
=
URLSession
.
shared
let
dataTask
=
urlSession
.
dataTask
(
with
:
url
)
{
[
weak
self
]
(
data
,
reponse
,
error
)
in
guard
let
self
=
self
else
{
return
}
let
dataTask
=
urlSession
.
dataTask
(
with
:
request
)
{
(
data
,
reponse
,
error
)
in
// TODO: check response HTTP code
guard
let
data
=
data
else
{
guard
let
data
=
data
else
{
completion
(
nil
,
BlendHealthSourceError
.
networkError
(
error
))
completion
(
nil
,
BlendHealthSourceError
.
networkError
(
error
))
return
return
...
...
1Weather/Network/Health/Model/BlendHealthModels.swift
View file @
1f4dd173
...
@@ -10,14 +10,12 @@ import UIKit
...
@@ -10,14 +10,12 @@ import UIKit
// MARK: - HealthCenter
// MARK: - HealthCenter
struct
BlendHealthCenter
:
Codable
{
struct
BlendHealthCenter
:
Codable
{
public
let
s2CellID
:
String
public
let
updatedOn
:
Date
public
let
updatedOn
:
Date
public
let
airQuality
:
BlendAirQuality
?
public
let
airQuality
:
BlendAirQuality
?
public
let
fire
:
BlendFire
public
let
fire
:
BlendFire
public
let
pollutants
,
pollen
:
[
BlendPoll
]?
public
let
pollutants
,
pollen
:
[
BlendPoll
]?
enum
CodingKeys
:
String
,
CodingKey
{
enum
CodingKeys
:
String
,
CodingKey
{
case
s2CellID
=
"s2_cell_id"
case
updatedOn
=
"updated_on"
case
updatedOn
=
"updated_on"
case
airQuality
=
"air_quality"
case
airQuality
=
"air_quality"
case
fire
,
pollutants
,
pollen
case
fire
,
pollutants
,
pollen
...
@@ -33,7 +31,7 @@ struct BlendHealthCenter: Codable {
...
@@ -33,7 +31,7 @@ struct BlendHealthCenter: Codable {
return
dict
return
dict
}
}
let
result
=
Health
(
lastUpdateTime
:
updatedOn
,
airQuality
:
airQuality
,
pollutants
:
pollutants
??
[:])
let
result
=
Health
(
lastUpdateTime
:
Date
()
,
airQuality
:
airQuality
,
pollutants
:
pollutants
??
[:])
return
result
return
result
}
}
}
}
...
...
1Weather/UI/View controllers/Today/Cells/TodayAirQualityCell/TodayAirQualityCell.swift
View file @
1f4dd173
...
@@ -40,10 +40,10 @@ class TodayAirQualityCell: UITableViewCell {
...
@@ -40,10 +40,10 @@ class TodayAirQualityCell: UITableViewCell {
fatalError
(
"init(coder:) has not been implemented"
)
fatalError
(
"init(coder:) has not been implemented"
)
}
}
public
func
configure
(
health
:
Health
)
{
public
func
configure
(
health
:
Health
?
)
{
airQualityValueLabel
.
text
=
"
\(
Int
(
health
.
airQuality
?
.
index
??
0
)
)
"
airQualityValueLabel
.
text
=
"
\(
Int
(
health
?
.
airQuality
?
.
index
??
0
)
)
"
let
aqiText
=
"air.quality.is"
.
localized
()
let
aqiText
=
"air.quality.is"
.
localized
()
let
aqiConditionText
=
health
.
airQuality
?
.
status
.
localized
??
""
let
aqiConditionText
=
health
?
.
airQuality
?
.
status
.
localized
??
""
let
attrString
=
NSMutableAttributedString
(
string
:
"
\(
aqiText
)\n\(
aqiConditionText
)
"
,
let
attrString
=
NSMutableAttributedString
(
string
:
"
\(
aqiText
)\n\(
aqiConditionText
)
"
,
attributes
:
[
.
font
:
AppFont
.
SFPro
.
regular
(
size
:
24
),
attributes
:
[
.
font
:
AppFont
.
SFPro
.
regular
(
size
:
24
),
.
foregroundColor
:
ThemeManager
.
currentTheme
.
secondaryTextColor
])
.
foregroundColor
:
ThemeManager
.
currentTheme
.
secondaryTextColor
])
...
@@ -54,7 +54,7 @@ class TodayAirQualityCell: UITableViewCell {
...
@@ -54,7 +54,7 @@ class TodayAirQualityCell: UITableViewCell {
//Fill pollutions
//Fill pollutions
stackView
.
removeAll
()
stackView
.
removeAll
()
health
.
pollutants
.
map
{
$1
}
.
forEach
{
health
?
.
pollutants
.
map
{
$1
}
.
forEach
{
let
pollutionView
=
PollutantView
()
let
pollutionView
=
PollutantView
()
pollutionView
.
configure
(
pollutant
:
$0
)
pollutionView
.
configure
(
pollutant
:
$0
)
stackView
.
addArrangedSubview
(
pollutionView
)
stackView
.
addArrangedSubview
(
pollutionView
)
...
...
1Weather/UI/View controllers/Today/Cells/TodayCellFactory.swift
View file @
1f4dd173
...
@@ -40,12 +40,12 @@ class TodayCellFactory: CellFactoryProtocol {
...
@@ -40,12 +40,12 @@ class TodayCellFactory: CellFactoryProtocol {
private
var
todaySection
=
TodaySection
(
rows
:
[
.
alert
,
.
forecast
,
.
ad
,
private
var
todaySection
=
TodaySection
(
rows
:
[
.
alert
,
.
forecast
,
.
ad
,
.
conditions
,
.
forecastPeriod
,
.
precipitation
,
.
conditions
,
.
forecastPeriod
,
.
precipitation
,
.
airQuality
,
.
dayTime
,
.
sun
,
.
moon
])
.
airQuality
,
.
dayTime
,
.
sun
,
.
moon
])
private
let
health
=
Health
(
lastUpdateTime
:
Date
(),
//
private let health = Health(lastUpdateTime: Date(),
airQuality
:
.
init
(
index
:
48
,
advice
:
"some"
),
//
airQuality: .init(index: 48, advice: "some"),
pollutants
:
[
"pm25"
:
.
init
(
name
:
"PM 2.5"
,
value
:
48
),
//
pollutants: ["pm25" : .init(name: "PM 2.5", value: 48),
"pm10"
:
.
init
(
name
:
"PM 10"
,
value
:
42
),
//
"pm10" : .init(name: "PM 10", value: 42),
"no2"
:
.
init
(
name
:
"NO2"
,
value
:
74
),
//
"no2" : .init(name: "NO2", value: 74),
"so2"
:
.
init
(
name
:
"SO2"
,
value
:
135
)])
//
"so2" : .init(name: "SO2", value: 135)])
//Public
//Public
init
(
viewModel
:
TodayViewModel
)
{
init
(
viewModel
:
TodayViewModel
)
{
...
@@ -112,7 +112,7 @@ class TodayCellFactory: CellFactoryProtocol {
...
@@ -112,7 +112,7 @@ class TodayCellFactory: CellFactoryProtocol {
return
cell
return
cell
case
.
airQuality
:
case
.
airQuality
:
let
cell
=
dequeueReusableCell
(
type
:
TodayAirQualityCell
.
self
,
tableView
:
tableView
,
indexPath
:
indexPath
)
let
cell
=
dequeueReusableCell
(
type
:
TodayAirQualityCell
.
self
,
tableView
:
tableView
,
indexPath
:
indexPath
)
cell
.
configure
(
health
:
self
.
health
)
cell
.
configure
(
health
:
loc
.
health
)
return
cell
return
cell
case
.
dayTime
:
case
.
dayTime
:
let
cell
=
dequeueReusableCell
(
type
:
TodayDayTimesCell
.
self
,
tableView
:
tableView
,
indexPath
:
indexPath
)
let
cell
=
dequeueReusableCell
(
type
:
TodayDayTimesCell
.
self
,
tableView
:
tableView
,
indexPath
:
indexPath
)
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment