-
Notifications
You must be signed in to change notification settings - Fork 3
/
KrakowPiosDataLoader.swift
145 lines (115 loc) · 4.86 KB
/
KrakowPiosDataLoader.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
//
// KrakowPiosDataLoader.swift
// SmogWatch WatchKit Extension
//
// Created by Kuba Suder on 23.12.2018.
// Copyright © 2018 Kuba Suder. Licensed under WTFPL license.
//
import Foundation
import os.log
import WatchKit
private let dataURL = "http://monitoring.krakow.pios.gov.pl/dane-pomiarowe/pobierz"
private let log = OSLog(subsystem: OSLog.subsystem, category: "Data Loader")
private struct Response: Decodable {
let data: ResponseData
struct ResponseData: Decodable {
let series: [DataSeries]
struct DataSeries: Decodable {
enum CodingKeys: String, CodingKey {
case points = "data"
}
let points: [DataPoint]
struct DataPoint: Decodable {
let date: Date
let value: Double
struct InvalidValueError: Error {}
init(from decoder: Decoder) throws {
var container = try decoder.unkeyedContainer()
if let timestamp = try Int(container.decode(String.self)) {
self.date = Date(timeIntervalSince1970: TimeInterval(timestamp))
} else {
throw InvalidValueError()
}
if let value = try Double(container.decode(String.self)) {
self.value = value
} else {
throw InvalidValueError()
}
}
}
}
}
}
class KrakowPiosDataLoader {
let dateFormatter: DateFormatter = {
let d = DateFormatter()
d.locale = Locale(identifier: "en_US_POSIX")
d.dateFormat = "dd.MM.yyyy"
d.timeZone = TimeZone(identifier: "Europe/Warsaw")!
return d
}()
let session: URLSession = {
let config = URLSessionConfiguration.ephemeral
config.timeoutIntervalForResource = 10.0
return URLSession(configuration: config)
}()
let dataStore = DataStore()
func queryString(channelId: Int, date: Date? = nil) -> String {
// data is usually around one hour behind, so at midnight we need to ask for the previous day
let oneHourAgo = Calendar(identifier: .gregorian).date(byAdding: .hour, value: -1, to: Date())!
let query: [String:Any] = [
"measType": "Auto",
"viewType": "Parameter",
"dateRange": "Day",
"date": dateFormatter.string(from: date ?? oneHourAgo),
"viewTypeEntityId": "pm10",
"channels": [channelId]
]
let jsonData = try! JSONSerialization.data(withJSONObject: query, options: [])
let json = String(data: jsonData, encoding: .utf8)!
return "query=\(json)"
}
func fetchData(date: Date? = nil, _ completion: @escaping (Bool) -> ()) {
guard let channelId = dataStore.selectedChannelId else {
os_log("No channel selected", log: log, type: .error)
completion(false)
return
}
let query = queryString(channelId: channelId, date: date)
var request = URLRequest(url: URL(string: dataURL)!)
request.httpBody = query.data(using: .utf8)!
request.httpMethod = "POST"
os_log("Sending request [state: %{public}@] to %{public}@ with %{public}@ ...", log: log,
WKExtension.shared().applicationState.description, dataURL, query)
let task = session.dataTask(with: request) { (data, response, error) in
var success = false
os_log("Response received: %{public}@ %{public}@ %{public}@", log: log,
data != nil ? "\(data!.count) bytes" : "(nil)",
response != nil ? "\(response!)" : "(nil)",
error != nil ? "\(error!)" : "(no error)")
if let data = data {
if let response = try? JSONDecoder().decode(Response.self, from: data) {
if let series = response.data.series.first {
if let lastPoint = series.points.last {
self.dataStore.addPoints(
series.points.map({ DataPoint(date: $0.date, value: $0.value )})
)
if date == nil {
self.dataStore.lastUpdateDate = Date()
os_log("Saving data: %.0f at %@", log: log, lastPoint.value, lastPoint.date as NSDate)
} else {
os_log("Added data from %@", log: log, date! as NSDate)
}
success = true
}
}
}
}
if !success {
os_log("KrakowPiosDataLoader: no data found", log: log)
}
completion(success)
}
task.resume()
}
}