Commit 85443f4e by Dmitriy Stepanets

Implementing minutely API

parent b50d56fd
...@@ -198,6 +198,8 @@ ...@@ -198,6 +198,8 @@
CDE2BF222609D4250085C930 /* ForecastWindSpeedCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDE2BF212609D4250085C930 /* ForecastWindSpeedCell.swift */; }; CDE2BF222609D4250085C930 /* ForecastWindSpeedCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDE2BF212609D4250085C930 /* ForecastWindSpeedCell.swift */; };
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 */; };
CDF079FE26D501BD00E797D9 /* BlendMinutelySource.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CDF079FD26D501BD00E797D9 /* BlendMinutelySource.framework */; };
CDF079FF26D501BD00E797D9 /* BlendMinutelySource.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = CDF079FD26D501BD00E797D9 /* BlendMinutelySource.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
CDF63D29266779D8003DE569 /* AdLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDF63D28266779D8003DE569 /* AdLogger.swift */; }; CDF63D29266779D8003DE569 /* AdLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDF63D28266779D8003DE569 /* AdLogger.swift */; };
CDF6E87726A8329D004A9DBD /* WindWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDF6E87626A8329D004A9DBD /* WindWidget.swift */; }; CDF6E87726A8329D004A9DBD /* WindWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDF6E87626A8329D004A9DBD /* WindWidget.swift */; };
CDF8F12A262089A200DB384A /* MapTimeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDF8F129262089A200DB384A /* MapTimeView.swift */; }; CDF8F12A262089A200DB384A /* MapTimeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDF8F129262089A200DB384A /* MapTimeView.swift */; };
...@@ -284,6 +286,7 @@ ...@@ -284,6 +286,7 @@
CDFE45BD26566EF50021A29F /* WDTWeatherSource.framework in Embed Frameworks */, CDFE45BD26566EF50021A29F /* WDTWeatherSource.framework in Embed Frameworks */,
CD427D28266F856700B4350A /* InMobiShortsSource.framework in Embed Frameworks */, CD427D28266F856700B4350A /* InMobiShortsSource.framework in Embed Frameworks */,
CEEF4101265E47FF00425D8F /* BlendFIPSSource.framework in Embed Frameworks */, CEEF4101265E47FF00425D8F /* BlendFIPSSource.framework in Embed Frameworks */,
CDF079FF26D501BD00E797D9 /* BlendMinutelySource.framework in Embed Frameworks */,
CE13B97C2626FB11007CBD4D /* PSMLocationSDK.xcframework in Embed Frameworks */, CE13B97C2626FB11007CBD4D /* PSMLocationSDK.xcframework in Embed Frameworks */,
CE30E3802668FBE3006DF5CD /* OneWeatherAnalytics.framework in Embed Frameworks */, CE30E3802668FBE3006DF5CD /* OneWeatherAnalytics.framework in Embed Frameworks */,
CD615F7F265523BD00B717DB /* OneWeatherCore.framework in Embed Frameworks */, CD615F7F265523BD00B717DB /* OneWeatherCore.framework in Embed Frameworks */,
...@@ -496,6 +499,7 @@ ...@@ -496,6 +499,7 @@
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>"; };
CDEF70E2266E10B600BA40D6 /* OneWeatherCore.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = OneWeatherCore.framework; sourceTree = BUILT_PRODUCTS_DIR; }; CDEF70E2266E10B600BA40D6 /* OneWeatherCore.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = OneWeatherCore.framework; sourceTree = BUILT_PRODUCTS_DIR; };
CDF079FD26D501BD00E797D9 /* BlendMinutelySource.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = BlendMinutelySource.framework; sourceTree = BUILT_PRODUCTS_DIR; };
CDF63D28266779D8003DE569 /* AdLogger.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AdLogger.swift; sourceTree = "<group>"; }; CDF63D28266779D8003DE569 /* AdLogger.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AdLogger.swift; sourceTree = "<group>"; };
CDF6E87626A8329D004A9DBD /* WindWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WindWidget.swift; sourceTree = "<group>"; }; CDF6E87626A8329D004A9DBD /* WindWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WindWidget.swift; sourceTree = "<group>"; };
CDF8F129262089A200DB384A /* MapTimeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapTimeView.swift; sourceTree = "<group>"; }; CDF8F129262089A200DB384A /* MapTimeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapTimeView.swift; sourceTree = "<group>"; };
...@@ -566,6 +570,7 @@ ...@@ -566,6 +570,7 @@
CD3884552657BA8B0070FD6F /* CoreDataStorage.framework in Frameworks */, CD3884552657BA8B0070FD6F /* CoreDataStorage.framework in Frameworks */,
CD3884832657BBCC0070FD6F /* DelayedSaveStorage.framework in Frameworks */, CD3884832657BBCC0070FD6F /* DelayedSaveStorage.framework in Frameworks */,
CEEF4100265E47FF00425D8F /* BlendFIPSSource.framework in Frameworks */, CEEF4100265E47FF00425D8F /* BlendFIPSSource.framework in Frameworks */,
CDF079FE26D501BD00E797D9 /* BlendMinutelySource.framework in Frameworks */,
CD615F7E265523BD00B717DB /* OneWeatherCore.framework in Frameworks */, CD615F7E265523BD00B717DB /* OneWeatherCore.framework in Frameworks */,
CE30E37F2668FBE3006DF5CD /* OneWeatherAnalytics.framework in Frameworks */, CE30E37F2668FBE3006DF5CD /* OneWeatherAnalytics.framework in Frameworks */,
9FAD89D1BEBA0FEB5F50BE73 /* Pods_1Weather.framework in Frameworks */, 9FAD89D1BEBA0FEB5F50BE73 /* Pods_1Weather.framework in Frameworks */,
...@@ -1458,6 +1463,7 @@ ...@@ -1458,6 +1463,7 @@
DBFD169AA2AA6A3CB5B68BB5 /* Frameworks */ = { DBFD169AA2AA6A3CB5B68BB5 /* Frameworks */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
CDF079FD26D501BD00E797D9 /* BlendMinutelySource.framework */,
CD5909CF26A59AAA00448579 /* OneWeatherUI.framework */, CD5909CF26A59AAA00448579 /* OneWeatherUI.framework */,
CD5181BF269EEB61008E6B04 /* CoreLocation.framework */, CD5181BF269EEB61008E6B04 /* CoreLocation.framework */,
CDEF70E2266E10B600BA40D6 /* OneWeatherCore.framework */, CDEF70E2266E10B600BA40D6 /* OneWeatherCore.framework */,
......
...@@ -17,6 +17,9 @@ ...@@ -17,6 +17,9 @@
location = "group:BlendHealthSource/BlendHealthSource.xcodeproj"> location = "group:BlendHealthSource/BlendHealthSource.xcodeproj">
</FileRef> </FileRef>
<FileRef <FileRef
location = "group:BlendMinutelySource/BlendMinutelySource.xcodeproj">
</FileRef>
<FileRef
location = "group:CoreDataStorage/CoreDataStorage.xcodeproj"> location = "group:CoreDataStorage/CoreDataStorage.xcodeproj">
</FileRef> </FileRef>
<FileRef <FileRef
......
...@@ -3,4 +3,54 @@ ...@@ -3,4 +3,54 @@
uuid = "55281C35-FE9F-4CED-865E-FBED0E7393F6" uuid = "55281C35-FE9F-4CED-865E-FBED0E7393F6"
type = "0" type = "0"
version = "2.0"> version = "2.0">
<Breakpoints>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
uuid = "BD6801C5-CD0B-467D-9B94-1250A8FA6E1D"
shouldBeEnabled = "Yes"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "BlendMinutelySource/BlendMinutelySource/BlendMinutelySource.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "49"
endingLineNumber = "49"
landmarkName = "getMinutelyForecast(forLocation:completion:)"
landmarkType = "7">
</BreakpointContent>
</BreakpointProxy>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
uuid = "A2A04C12-64A5-4FC6-A9A4-2CECEE6DBFE9"
shouldBeEnabled = "Yes"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "OneWeatherCore/OneWeatherCore/Model/LocationManager.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "378"
endingLineNumber = "378"
landmarkName = "updateMinutelyForecast(for:)"
landmarkType = "7">
</BreakpointContent>
</BreakpointProxy>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
uuid = "958DC1E7-7576-44D0-B8D7-5CD5ECB0CDC1"
shouldBeEnabled = "Yes"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "OneWeatherCore/OneWeatherCore/Model/LocationManager.swift"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "318"
endingLineNumber = "318"
landmarkName = "updateEverythingIfNeeded()"
landmarkType = "7">
</BreakpointContent>
</BreakpointProxy>
</Breakpoints>
</Bucket> </Bucket>
...@@ -21,6 +21,7 @@ import OneWeatherUI ...@@ -21,6 +21,7 @@ import OneWeatherUI
import WDTWeatherSource import WDTWeatherSource
import BlendHealthSource import BlendHealthSource
import BlendMinutelySource
import BlendFIPSSource import BlendFIPSSource
import CoreDataStorage import CoreDataStorage
import DelayedSaveStorage import DelayedSaveStorage
...@@ -59,6 +60,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { ...@@ -59,6 +60,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
//TODO: introduce dependency management (dependency injection engine coupled with a factory or something of the sort). //TODO: introduce dependency management (dependency injection engine coupled with a factory or something of the sort).
LocationManager.shared = LocationManager(weatherUpdateSource: WdtWeatherSource(), LocationManager.shared = LocationManager(weatherUpdateSource: WdtWeatherSource(),
healthSource: BlendHealthSource(), healthSource: BlendHealthSource(),
minutelyForecastSource: BlendMinutelySource(),
nwsAlertsManager: NWSAlertsManager(), nwsAlertsManager: NWSAlertsManager(),
fipsSource: BlendFIPSSource(), fipsSource: BlendFIPSSource(),
pushNotificationsManager: PushNotificationsManager.shared, pushNotificationsManager: PushNotificationsManager.shared,
......
//
// BlendMinutelySource.h
// BlendMinutelySource
//
// Created by Dmitry Stepanets on 24.08.2021.
//
#import <Foundation/Foundation.h>
//! Project version number for BlendMinutelySource.
FOUNDATION_EXPORT double BlendMinutelySourceVersionNumber;
//! Project version string for BlendMinutelySource.
FOUNDATION_EXPORT const unsigned char BlendMinutelySourceVersionString[];
// In this header, you should import all the public headers of your framework using statements like #import <BlendMinutelySource/PublicHeader.h>
//
// BlendMinutelySource.swift
// BlendMinutelySource
//
// Created by Dmitry Stepanets on 24.08.2021.
//
import Foundation
import OneWeatherCore
public enum BlendMinutelySourceError: Error {
case badUrl
case networkError(Error?)
case badServerResponse(Error?)
case dataEncodingError(String)
case alreadyBeingUpdated
}
public class BlendMinutelySource: MinutelyForecastSource {
//Private
private let kBlendApiKey = "0imfnc8mVLWwsAawjYr4Rx-Af50DDqtlx"
private let kEndpoitURL = "https://pro-1w-dataaggregator.onelouder.com/1weather/api/v1/weather/nowcast"
private let kCountry = "US"
private let kWindUnit = "mph"
private let kPressureUnit = "inHg"
private let kPrecipitationUnit = "inhr"
private let kTempUnit = "F"
private lazy var dateFormatter: DateFormatter = {
let fmt = DateFormatter()
fmt.dateFormat = "yyyy-MM-dd'T'hh:mm:ss.sss'Z'"
return fmt
}()
//Public
public init() {}
public func getMinutelyForecast(forLocation location: Location, completion: @escaping MinutelyForecastCompletion) {
let endpointURL = URL(string: kEndpoitURL)!
let queryItems = [URLQueryItem(name: "lat", value: location.lat),
URLQueryItem(name: "lon", value: location.lon),
URLQueryItem(name: "state", value: location.region),
URLQueryItem(name: "country", value: kCountry),
URLQueryItem(name: "city", value: location.cityName),
URLQueryItem(name: "wind_unit", value: kWindUnit),
URLQueryItem(name: "pressure_unit", value: kPressureUnit),
URLQueryItem(name: "prec_unit", value: kPrecipitationUnit),
URLQueryItem(name: "temp_unit", value: kTempUnit)]
guard var components = URLComponents(url: endpointURL, resolvingAgainstBaseURL: true) else {
completion(.failure(BlendMinutelySourceError.badUrl))
return
}
components.queryItems = queryItems
guard let requestURL = components.url else {
completion(.failure(BlendMinutelySourceError.badUrl))
return
}
var request = URLRequest(url: requestURL)
request.addValue(kBlendApiKey, forHTTPHeaderField: "blend-api-key")
URLSession.shared.dataTask(with: request) {[weak self] data, response, error in
guard let self = self else {
completion(.failure(BlendMinutelySourceError.dataEncodingError("Missing self")))
return
}
if let networkError = error {
completion(.failure(BlendMinutelySourceError.networkError(networkError)))
return
}
guard let forecastData = data else {
completion(.failure(BlendMinutelySourceError.dataEncodingError("Incoming data is invalid")))
return
}
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
decoder.dateDecodingStrategy = .formatted(self.dateFormatter)
do {
let blendForecast = try decoder.decode(BlendMinutelyForecast.self, from: forecastData)
let forecast = MinutelyForecast(lastUpdateTime: Date(),
forecastInterval: blendForecast.forecastInterval,
tempUnit: blendForecast.tempUnit,
windUnit: blendForecast.windUnit,
pressureUnit: blendForecast.pressureUnit,
forecast: blendForecast.forecast.map{ MinutelyItem(time: $0.time,
temp: $0.temp,
precipitation: $0.precipitation,
windSpeed: $0.windSpeed,
pressure: $0.pressure) })
completion(.success(forecast))
}
catch {
completion(.failure(BlendMinutelySourceError.dataEncodingError(error.localizedDescription)))
}
}
.resume()
}
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
</dict>
</plist>
//
// BlendMinutelyForecast.swift
// BlendMinutelySource
//
// Created by Dmitry Stepanets on 24.08.2021.
//
import Foundation
import OneWeatherCore
struct BlendMinutelyForecast: Codable {
public let forecastInterval: Int
public let tempUnit: String
public let windUnit: String
public let pressureUnit: String
public let forecast: [BlendMinutelyItem]
}
//
// BlendMinutelyItem.swift
// BlendMinutelySource
//
// Created by Dmitry Stepanets on 24.08.2021.
//
import Foundation
public struct BlendMinutelyItem: Codable {
let time: Date
let temp: Int
let precipitation: Double
let windSpeed: Int
let pressure: Int
}
//
// BlendMinutelySourceTests.swift
// BlendMinutelySourceTests
//
// Created by Dmitry Stepanets on 24.08.2021.
//
import XCTest
@testable import BlendMinutelySource
class BlendMinutelySourceTests: XCTestCase {
override func setUpWithError() throws {
// Put setup code here. This method is called before the invocation of each test method in the class.
}
override func tearDownWithError() throws {
// Put teardown code here. This method is called after the invocation of each test method in the class.
}
func testExample() throws {
// This is an example of a functional test case.
// Use XCTAssert and related functions to verify your tests produce the correct results.
}
func testPerformanceExample() throws {
// This is an example of a performance test case.
self.measure {
// Put the code you want to measure the time of here.
}
}
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
</dict>
</plist>
...@@ -74,6 +74,9 @@ ...@@ -74,6 +74,9 @@
CDD2F8EF2665102B00B48322 /* LocationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDD2F8EE2665102B00B48322 /* LocationManager.swift */; }; CDD2F8EF2665102B00B48322 /* LocationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDD2F8EE2665102B00B48322 /* LocationManager.swift */; };
CDD2F8F12665112900B48322 /* DeviceLocationMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDD2F8F02665112800B48322 /* DeviceLocationMonitor.swift */; }; CDD2F8F12665112900B48322 /* DeviceLocationMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDD2F8F02665112800B48322 /* DeviceLocationMonitor.swift */; };
CDD2F8F62665117400B48322 /* NWSAlertsManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDD2F8F42665117400B48322 /* NWSAlertsManager.swift */; }; CDD2F8F62665117400B48322 /* NWSAlertsManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDD2F8F42665117400B48322 /* NWSAlertsManager.swift */; };
CDF07A0126D5027300E797D9 /* MinutelyForecastSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDF07A0026D5027300E797D9 /* MinutelyForecastSource.swift */; };
CDF07A0526D5032800E797D9 /* MinutelyItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDF07A0326D5032800E797D9 /* MinutelyItem.swift */; };
CDF07A0626D5032800E797D9 /* MinutelyForecast.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDF07A0426D5032800E797D9 /* MinutelyForecast.swift */; };
CDFE458D26566BD50021A29F /* Storage.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDFE458C26566BD50021A29F /* Storage.swift */; }; CDFE458D26566BD50021A29F /* Storage.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDFE458C26566BD50021A29F /* Storage.swift */; };
CDFE459426566D7B0021A29F /* HealthSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDFE459326566D7B0021A29F /* HealthSource.swift */; }; CDFE459426566D7B0021A29F /* HealthSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDFE459326566D7B0021A29F /* HealthSource.swift */; };
CDFE459626566D860021A29F /* FIPSSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDFE459526566D860021A29F /* FIPSSource.swift */; }; CDFE459626566D860021A29F /* FIPSSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDFE459526566D860021A29F /* FIPSSource.swift */; };
...@@ -174,6 +177,9 @@ ...@@ -174,6 +177,9 @@
CDD2F8EE2665102B00B48322 /* LocationManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationManager.swift; sourceTree = "<group>"; }; CDD2F8EE2665102B00B48322 /* LocationManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationManager.swift; sourceTree = "<group>"; };
CDD2F8F02665112800B48322 /* DeviceLocationMonitor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeviceLocationMonitor.swift; sourceTree = "<group>"; }; CDD2F8F02665112800B48322 /* DeviceLocationMonitor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeviceLocationMonitor.swift; sourceTree = "<group>"; };
CDD2F8F42665117400B48322 /* NWSAlertsManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NWSAlertsManager.swift; sourceTree = "<group>"; }; CDD2F8F42665117400B48322 /* NWSAlertsManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NWSAlertsManager.swift; sourceTree = "<group>"; };
CDF07A0026D5027300E797D9 /* MinutelyForecastSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MinutelyForecastSource.swift; sourceTree = "<group>"; };
CDF07A0326D5032800E797D9 /* MinutelyItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MinutelyItem.swift; sourceTree = "<group>"; };
CDF07A0426D5032800E797D9 /* MinutelyForecast.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MinutelyForecast.swift; sourceTree = "<group>"; };
CDFE458C26566BD50021A29F /* Storage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Storage.swift; sourceTree = "<group>"; }; CDFE458C26566BD50021A29F /* Storage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Storage.swift; sourceTree = "<group>"; };
CDFE459326566D7B0021A29F /* HealthSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HealthSource.swift; sourceTree = "<group>"; }; CDFE459326566D7B0021A29F /* HealthSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HealthSource.swift; sourceTree = "<group>"; };
CDFE459526566D860021A29F /* FIPSSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FIPSSource.swift; sourceTree = "<group>"; }; CDFE459526566D860021A29F /* FIPSSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FIPSSource.swift; sourceTree = "<group>"; };
...@@ -363,6 +369,7 @@ ...@@ -363,6 +369,7 @@
CD615F912655269200B717DB /* Health */, CD615F912655269200B717DB /* Health */,
CD615F932655269200B717DB /* Notifications */, CD615F932655269200B717DB /* Notifications */,
CD3883EA2657B82A0070FD6F /* FIPS */, CD3883EA2657B82A0070FD6F /* FIPS */,
CDF07A0226D502F500E797D9 /* Minutely */,
CD427D1A266F5F0500B4350A /* Shorts */, CD427D1A266F5F0500B4350A /* Shorts */,
); );
path = ModelObjects; path = ModelObjects;
...@@ -439,6 +446,15 @@ ...@@ -439,6 +446,15 @@
path = Managers; path = Managers;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
CDF07A0226D502F500E797D9 /* Minutely */ = {
isa = PBXGroup;
children = (
CDF07A0426D5032800E797D9 /* MinutelyForecast.swift */,
CDF07A0326D5032800E797D9 /* MinutelyItem.swift */,
);
path = Minutely;
sourceTree = "<group>";
};
CDFE458B26566BC20021A29F /* Storage */ = { CDFE458B26566BC20021A29F /* Storage */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
...@@ -457,6 +473,7 @@ ...@@ -457,6 +473,7 @@
CDFE459526566D860021A29F /* FIPSSource.swift */, CDFE459526566D860021A29F /* FIPSSource.swift */,
CDFE459326566D7B0021A29F /* HealthSource.swift */, CDFE459326566D7B0021A29F /* HealthSource.swift */,
CD427D18266F5DCE00B4350A /* ShortsSource.swift */, CD427D18266F5DCE00B4350A /* ShortsSource.swift */,
CDF07A0026D5027300E797D9 /* MinutelyForecastSource.swift */,
); );
path = Sources; path = Sources;
sourceTree = "<group>"; sourceTree = "<group>";
...@@ -645,6 +662,7 @@ ...@@ -645,6 +662,7 @@
CDD2F8F12665112900B48322 /* DeviceLocationMonitor.swift in Sources */, CDD2F8F12665112900B48322 /* DeviceLocationMonitor.swift in Sources */,
CD2D55D626553384007B70F4 /* UserDefaultsValue.swift in Sources */, CD2D55D626553384007B70F4 /* UserDefaultsValue.swift in Sources */,
CD550FBA265531A100257FB5 /* RadarLayer.swift in Sources */, CD550FBA265531A100257FB5 /* RadarLayer.swift in Sources */,
CDF07A0626D5032800E797D9 /* MinutelyForecast.swift in Sources */,
CD550FBB265531A100257FB5 /* RadarLayerType.swift in Sources */, CD550FBB265531A100257FB5 /* RadarLayerType.swift in Sources */,
CDBC243F2656740E00F9F4E2 /* AppData.swift in Sources */, CDBC243F2656740E00F9F4E2 /* AppData.swift in Sources */,
CD550FBC265531A100257FB5 /* WeatherLayerType.swift in Sources */, CD550FBC265531A100257FB5 /* WeatherLayerType.swift in Sources */,
...@@ -661,6 +679,7 @@ ...@@ -661,6 +679,7 @@
CD91685726552FAE00EC04EF /* MulticastDelegate.swift in Sources */, CD91685726552FAE00EC04EF /* MulticastDelegate.swift in Sources */,
CDFE458D26566BD50021A29F /* Storage.swift in Sources */, CDFE458D26566BD50021A29F /* Storage.swift in Sources */,
CD615FBD2655295C00B717DB /* Measurement+String.swift in Sources */, CD615FBD2655295C00B717DB /* Measurement+String.swift in Sources */,
CDF07A0526D5032800E797D9 /* MinutelyItem.swift in Sources */,
CD615FBE2655295C00B717DB /* Calendar+TimeZone.swift in Sources */, CD615FBE2655295C00B717DB /* Calendar+TimeZone.swift in Sources */,
CD71B9C6265E629D00803DBB /* String+NewLine.swift in Sources */, CD71B9C6265E629D00803DBB /* String+NewLine.swift in Sources */,
CD91685F26552FEC00EC04EF /* Global.swift in Sources */, CD91685F26552FEC00EC04EF /* Global.swift in Sources */,
...@@ -676,6 +695,7 @@ ...@@ -676,6 +695,7 @@
CD427D19266F5DCE00B4350A /* ShortsSource.swift in Sources */, CD427D19266F5DCE00B4350A /* ShortsSource.swift in Sources */,
CD2D55D8265533F4007B70F4 /* UserDefaultsWrapper.swift in Sources */, CD2D55D8265533F4007B70F4 /* UserDefaultsWrapper.swift in Sources */,
CDD2F8F62665117400B48322 /* NWSAlertsManager.swift in Sources */, CDD2F8F62665117400B48322 /* NWSAlertsManager.swift in Sources */,
CDF07A0126D5027300E797D9 /* MinutelyForecastSource.swift in Sources */,
CE3A112726CD3CDE00D925C7 /* UserDefaults+OneWeather.swift in Sources */, CE3A112726CD3CDE00D925C7 /* UserDefaults+OneWeather.swift in Sources */,
CD11AFE726651BF900EC4BA0 /* LegacyWdtLocation.swift in Sources */, CD11AFE726651BF900EC4BA0 /* LegacyWdtLocation.swift in Sources */,
CD11AFE326651B6300EC4BA0 /* LegacyMigrationManager.swift in Sources */, CD11AFE326651B6300EC4BA0 /* LegacyMigrationManager.swift in Sources */,
......
...@@ -23,6 +23,7 @@ public class LocationManager { ...@@ -23,6 +23,7 @@ public class LocationManager {
private let weatherUpdateSource: WeatherSource private let weatherUpdateSource: WeatherSource
private let healthSource: HealthSource private let healthSource: HealthSource
private let minutelyForecastSource: MinutelyForecastSource
private let fipsSource: FIPSSource private let fipsSource: FIPSSource
public let nwsAlertsManager: NWSAlertsManager public let nwsAlertsManager: NWSAlertsManager
private var pushNotificationsManager: PushNotificationsManagerProtocol? private var pushNotificationsManager: PushNotificationsManagerProtocol?
...@@ -209,9 +210,10 @@ public class LocationManager { ...@@ -209,9 +210,10 @@ public class LocationManager {
!locations.isEmpty || deviceLocationMonitor.hasLocationPermissions !locations.isEmpty || deviceLocationMonitor.hasLocationPermissions
} }
public init(weatherUpdateSource: WeatherSource, healthSource: HealthSource, nwsAlertsManager: NWSAlertsManager, fipsSource: FIPSSource, pushNotificationsManager: PushNotificationsManagerProtocol?, storage: Storage) { public init(weatherUpdateSource: WeatherSource, healthSource: HealthSource, minutelyForecastSource: MinutelyForecastSource, nwsAlertsManager: NWSAlertsManager, fipsSource: FIPSSource, pushNotificationsManager: PushNotificationsManagerProtocol?, storage: Storage) {
self.weatherUpdateSource = weatherUpdateSource self.weatherUpdateSource = weatherUpdateSource
self.healthSource = healthSource self.healthSource = healthSource
self.minutelyForecastSource = minutelyForecastSource
self.deviceLocationMonitor = DeviceLocationMonitor() self.deviceLocationMonitor = DeviceLocationMonitor()
self.nwsAlertsManager = nwsAlertsManager self.nwsAlertsManager = nwsAlertsManager
self.fipsSource = fipsSource self.fipsSource = fipsSource
...@@ -301,6 +303,7 @@ public class LocationManager { ...@@ -301,6 +303,7 @@ public class LocationManager {
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) updateHealth(for: defaultLocation)
updateMinutelyForecast(for: defaultLocation)
return return
} }
log.info("Update all \(locations.count) locations if needed...") log.info("Update all \(locations.count) locations if needed...")
...@@ -312,6 +315,7 @@ public class LocationManager { ...@@ -312,6 +315,7 @@ public class LocationManager {
updateWeather(for: location, updateType: .preferIncremental) updateWeather(for: location, updateType: .preferIncremental)
} }
updateHealth(for: location) updateHealth(for: location)
updateMinutelyForecast(for: location)
updateNotifications(for: location) updateNotifications(for: location)
getFipsIfNeeded(for: location) getFipsIfNeeded(for: location)
} }
...@@ -369,6 +373,12 @@ public class LocationManager { ...@@ -369,6 +373,12 @@ public class LocationManager {
} }
} }
public func updateMinutelyForecast(for location: Location) {
minutelyForecastSource.getMinutelyForecast(forLocation: location) { result in
print("Break")
}
}
public func updateNotifications(for location: Location) { public func updateNotifications(for location: Location) {
if let lastTimeUpdated = location.notifications?.updatedAt { if let lastTimeUpdated = location.notifications?.updatedAt {
guard Date().timeIntervalSince(lastTimeUpdated) >= nwsAlertsManager.updateInterval else { guard Date().timeIntervalSince(lastTimeUpdated) >= nwsAlertsManager.updateInterval else {
......
...@@ -47,6 +47,7 @@ public struct Location { ...@@ -47,6 +47,7 @@ public struct Location {
public var today: CurrentWeather? public var today: CurrentWeather?
public var daily = [DailyWeather]() public var daily = [DailyWeather]()
public var minutely: MinutelyForecast?
public var hourly = [HourlyWeather]() { public var hourly = [HourlyWeather]() {
didSet { didSet {
let calendar = Calendar.timeZoneCalendar(timeZone: self.timeZone) let calendar = Calendar.timeZoneCalendar(timeZone: self.timeZone)
......
//
// MinutelyForecast.swift
// OneWeatherCore
//
// Created by Dmitry Stepanets on 24.08.2021.
//
import Foundation
public struct MinutelyForecast: Codable {
public let lastUpdateTime: Date
public let forecastInterval: Int
public let tempUnit: Temperature
public let windUnit: WindSpeed
public let pressureUnit: Pressure
public let forecast: [MinutelyItem]
public init(lastUpdateTime: Date, forecastInterval: Int, tempUnit: Temperature, windUnit: WindSpeed, pressureUnit: Pressure, forecast: [MinutelyItem]) {
self.lastUpdateTime = lastUpdateTime
self.forecastInterval = forecastInterval
self.tempUnit = tempUnit
self.windUnit = windUnit
self.pressureUnit = pressureUnit
self.forecast = forecast
}
}
//
// MinutelyItem.swift
// OneWeatherCore
//
// Created by Dmitry Stepanets on 24.08.2021.
//
import Foundation
public struct MinutelyItem: Codable {
let time: Date
let temp: Int
let precipitation: Double
let windSpeed: Int
let pressure: Int
public init(time: Date, temp: Int, precipitation: Double, windSpeed: Int, pressure: Int) {
self.time = time
self.temp = temp
self.precipitation = precipitation
self.windSpeed = windSpeed
self.pressure = pressure
}
}
//
// MinutelyForecastSource.swift
// OneWeatherCore
//
// Created by Dmitry Stepanets on 24.08.2021.
//
import Foundation
public typealias MinutelyForecastCompletion = (_ result: Result<MinutelyForecast, Error>) -> ()
public protocol MinutelyForecastSource {
func getMinutelyForecast(forLocation location: Location, completion: @escaping MinutelyForecastCompletion)
}
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