Commit 6872d700 by Demid Merzlyakov

Model: work in progress.

parent c0f80491
......@@ -43,6 +43,8 @@
CDE18DD125D166F900C80ED9 /* ForecastViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDE18DD025D166F900C80ED9 /* ForecastViewController.swift */; };
CDE18DD825D16CB200C80ED9 /* NavigationCityButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDE18DD725D16CB200C80ED9 /* NavigationCityButton.swift */; };
CDEE8AD725DA882200C289DE /* PeriodForecastButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDEE8AD625DA882200C289DE /* PeriodForecastButton.swift */; };
CE9D181625ECB8370028D9D7 /* MulticastDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE9D181525ECB8370028D9D7 /* MulticastDelegate.swift */; };
CE9D181925ECB9A70028D9D7 /* Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE9D181825ECB9A70028D9D7 /* Logger.swift */; };
CEAD00A12577B2D5003596AD /* StuffThatIsPresentInTheMainProject.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEAD00A02577B2D5003596AD /* StuffThatIsPresentInTheMainProject.swift */; };
CEAFF08325DFC67F00DF4EBF /* Location.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEAFF08225DFC67F00DF4EBF /* Location.swift */; };
CEAFF08925DFC6B200DF4EBF /* CurrentWeather.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEAFF08825DFC6B200DF4EBF /* CurrentWeather.swift */; };
......@@ -56,6 +58,7 @@
CEC5270025E7BACB00DA58A5 /* WdtLocation.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEC526FF25E7BACB00DA58A5 /* WdtLocation.swift */; };
CEC5270325E7BB4000DA58A5 /* WdtSurfaceObservation.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEC5270225E7BB4000DA58A5 /* WdtSurfaceObservation.swift */; };
CEC5275D25E8E50B00DA58A5 /* WdtDailySummary.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEC5275C25E8E50B00DA58A5 /* WdtDailySummary.swift */; };
CEC5276025E92DDA00DA58A5 /* WdtHourlySummary.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEC5275F25E92DDA00DA58A5 /* WdtHourlySummary.swift */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
......@@ -99,6 +102,8 @@
CDE18DD025D166F900C80ED9 /* ForecastViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ForecastViewController.swift; sourceTree = "<group>"; };
CDE18DD725D16CB200C80ED9 /* NavigationCityButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationCityButton.swift; sourceTree = "<group>"; };
CDEE8AD625DA882200C289DE /* PeriodForecastButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PeriodForecastButton.swift; sourceTree = "<group>"; };
CE9D181525ECB8370028D9D7 /* MulticastDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MulticastDelegate.swift; sourceTree = "<group>"; };
CE9D181825ECB9A70028D9D7 /* Logger.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Logger.swift; sourceTree = "<group>"; };
CEAD00A02577B2D5003596AD /* StuffThatIsPresentInTheMainProject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StuffThatIsPresentInTheMainProject.swift; sourceTree = "<group>"; };
CEAFF08225DFC67F00DF4EBF /* Location.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Location.swift; sourceTree = "<group>"; };
CEAFF08825DFC6B200DF4EBF /* CurrentWeather.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CurrentWeather.swift; sourceTree = "<group>"; };
......@@ -112,6 +117,7 @@
CEC526FF25E7BACB00DA58A5 /* WdtLocation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WdtLocation.swift; sourceTree = "<group>"; };
CEC5270225E7BB4000DA58A5 /* WdtSurfaceObservation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WdtSurfaceObservation.swift; sourceTree = "<group>"; };
CEC5275C25E8E50B00DA58A5 /* WdtDailySummary.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WdtDailySummary.swift; sourceTree = "<group>"; };
CEC5275F25E92DDA00DA58A5 /* WdtHourlySummary.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WdtHourlySummary.swift; sourceTree = "<group>"; };
DF826CF4702D9DCCB9A9DD71 /* Pods-1Weather.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-1Weather.release.xcconfig"; path = "Target Support Files/Pods-1Weather/Pods-1Weather.release.xcconfig"; sourceTree = "<group>"; };
/* End PBXFileReference section */
......@@ -157,6 +163,7 @@
CD1237C1255D5C5900C98139 /* 1Weather */ = {
isa = PBXGroup;
children = (
CE9D181425ECB8370028D9D7 /* Common */,
CEAFF09925DFC78200DF4EBF /* Network */,
CEAFF08125DFC66F00DF4EBF /* Model */,
CD1237DA255D5DFA00C98139 /* PG.playground */,
......@@ -321,6 +328,15 @@
path = CityForecastTimePeriod;
sourceTree = "<group>";
};
CE9D181425ECB8370028D9D7 /* Common */ = {
isa = PBXGroup;
children = (
CE9D181825ECB9A70028D9D7 /* Logger.swift */,
CE9D181525ECB8370028D9D7 /* MulticastDelegate.swift */,
);
path = Common;
sourceTree = "<group>";
};
CEAFF08125DFC66F00DF4EBF /* Model */ = {
isa = PBXGroup;
children = (
......@@ -347,6 +363,7 @@
CEC526FF25E7BACB00DA58A5 /* WdtLocation.swift */,
CEC5270225E7BB4000DA58A5 /* WdtSurfaceObservation.swift */,
CEC5275C25E8E50B00DA58A5 /* WdtDailySummary.swift */,
CEC5275F25E92DDA00DA58A5 /* WdtHourlySummary.swift */,
);
path = Model;
sourceTree = "<group>";
......@@ -489,6 +506,7 @@
CEC526FD25E795F700DA58A5 /* WdtWeatherSource.swift in Sources */,
CEAFF09225DFC71D00DF4EBF /* HelperTypes.swift in Sources */,
CEAFF09C25DFC79F00DF4EBF /* WdtWeatherCode.swift in Sources */,
CE9D181925ECB9A70028D9D7 /* Logger.swift in Sources */,
CEAFF08925DFC6B200DF4EBF /* CurrentWeather.swift in Sources */,
CEC5270325E7BB4000DA58A5 /* WdtSurfaceObservation.swift in Sources */,
CEAFF08C25DFC6BD00DF4EBF /* DailyWeather.swift in Sources */,
......@@ -512,12 +530,14 @@
CDEE8AD725DA882200C289DE /* PeriodForecastButton.swift in Sources */,
CDE18DD125D166F900C80ED9 /* ForecastViewController.swift in Sources */,
CD39F2F525DE9571009FE398 /* ArrowButton.swift in Sources */,
CE9D181625ECB8370028D9D7 /* MulticastDelegate.swift in Sources */,
CD82300A25D6B2AF00A05501 /* AppTabBarController.swift in Sources */,
CD6B303E25726960004B34B3 /* ThemeProtocol.swift in Sources */,
CD6B303B2572680C004B34B3 /* SelfSizingButton.swift in Sources */,
CD9B6B1125DBC723001D9B80 /* CubicCurveAlgorithm.swift in Sources */,
CEC5270025E7BACB00DA58A5 /* WdtLocation.swift in Sources */,
CD15DB4225DA806C00024727 /* CityForecastTimePeriodCell.swift in Sources */,
CEC5276025E92DDA00DA58A5 /* WdtHourlySummary.swift in Sources */,
CDE18DCA25D165F100C80ED9 /* UITabBarController+Append.swift in Sources */,
CEC526FA25E7959A00DA58A5 /* WeatherSource.swift in Sources */,
CD822FFE25D6976F00A05501 /* TodayAdCell.swift in Sources */,
......
//
// Logger.swift
// OneWeather
//
// Created by Demid Merzlyakov on 18.01.2021.
// Copyright © 2021 OneLouder, Inc. All rights reserved.
//
import Foundation
final class Logger {
enum LogLevel {
case debug
case info
case warning
case error
}
static let prefix = "psm_1w"
var componentName: String
init(componentName: String) {
self.componentName = componentName
}
func log(level: LogLevel, message: String) {
#if DEBUG
NSLog("\(Logger.prefix) \(componentName) [\(logLevelString(level: level))]: \(message)")
#endif
}
func debug(_ message: String) {
log(level: .debug, message: message)
}
func info(_ message: String) {
log(level: .info, message: message)
}
func warning(_ message: String) {
log(level: .warning, message: message)
}
func error(_ message: String) {
log(level: .error, message: message)
}
private func logLevelString(level: LogLevel) -> String {
switch level {
case .debug:
return "DEBUG"
case .info:
return "INFO"
case .warning:
return "WARN"
case .error:
return "ERROR"
}
}
}
//
// MulticastDelegate.swift
// OneWeather
//
// Created by Demid Merzlyakov on 11.01.2021.
// Copyright © 2021 OneLouder, Inc. All rights reserved.
//
import Foundation
public class MulticastDelegate <T> {
public init() {}
private let delegates: NSHashTable<AnyObject> = NSHashTable.weakObjects()
public func add(delegate: T) {
if delegates.contains(delegate as AnyObject) {
return
}
delegates.add(delegate as AnyObject)
}
public func remove(delegate: T) {
for oneDelegate in delegates.allObjects.reversed() {
if oneDelegate === delegate as AnyObject {
delegates.remove(oneDelegate)
}
}
}
public func invoke(invocation: (T) -> ()) {
for delegate in delegates.allObjects.reversed() {
invocation(delegate as! T)
}
}
}
public func += <T: AnyObject> (left: MulticastDelegate<T>, right: T) {
left.add(delegate: right)
}
public func -= <T: AnyObject> (left: MulticastDelegate<T>, right: T) {
left.remove(delegate: right)
}
......@@ -7,6 +7,34 @@
import Foundation
class LocationManager {
public protocol LocationManagerDelegate: class {
func locationManager(_ locationManager: LocationManager, changedCurrentLocation newLocation: Location?)
}
public class LocationManager {
private let log = Logger(componentName: "LocationManager")
private let delegates = MulticastDelegate<LocationManagerDelegate>()
public var currentLocation: Location? {
didSet {
if currentLocation != oldValue {
log.info("Current location changed to: \(currentLocation?.description ?? "nil")")
delegates.invoke { [weak self] (delegate) in
guard let self = self else { return }
delegate.locationManager(self, changedCurrentLocation: currentLocation)
}
}
else {
log.debug("location updated without changing")
}
}
}
public func add(delegate: LocationManagerDelegate) {
delegates.add(delegate: delegate)
}
public func remove(delegate: LocationManagerDelegate) {
delegates.remove(delegate: delegate)
}
}
......@@ -8,7 +8,8 @@
import Foundation
public struct CurrentWeather {
public var date: String // since it depends on the local time, it's easier to store it as a string.
public var date: Date
public var timeZone: TimeZone
public var weekDay: WeekDay
public var type: WeatherType = .unknown
......@@ -21,7 +22,7 @@ public struct CurrentWeather {
public var temp: Temperature?
public var apparentTemp: Temperature?
public var humidity: Percent?
public var Visibility: Visibility?
public var visibility: Visibility?
public var pressure: Pressure?
public var sunrise: Time?
......
......@@ -8,7 +8,8 @@
import Foundation
public struct DailyWeather {
public var date: String // since it depends on the local time, it's easier to store it as a string.
public var date: Date
public var timeZone: TimeZone
public var weekDay: WeekDay
public var type: WeatherType = .unknown
......@@ -18,10 +19,10 @@ public struct DailyWeather {
public var windDirection: WindDirection?
public var precipitationProbability: Percent?
public var sunrise: Time?
public var sunset: Time?
public var sunrise: Date
public var sunset: Date
public var sunState: CelestialState = .normal
public var moonrise: Time?
public var moonset: Time?
public var moonrise: Date
public var moonset: Date
public var moonState: CelestialState = .normal
}
......@@ -77,14 +77,6 @@ public struct Time: CustomStringConvertible, Codable {
public var description: String {
var amOrPm = "am"
#if DEBUG
//TODO: REMOVE THIS'
TimeZone
#warning("THIS IS A DEBUG-ONLY PIECE OF CODE! Not even temporary! Remove before making a production build.")
#else
#error("THIS IS A DEBUG-ONLY PIECE OF CODE! Not even temporary! Remove before making a production build.")
#endif
var convertedHours = hours
if convertedHours > 12 {
amOrPm = "pm"
......@@ -139,6 +131,9 @@ public struct Time: CustomStringConvertible, Codable {
if !isAm {
hours = parsedHours + 12
}
else {
hours = parsedHours
}
}
}
else {
......@@ -150,7 +145,8 @@ public struct Time: CustomStringConvertible, Codable {
}
public func encode(to encoder: Encoder) throws {
encoder.singleValueContainer().encode(self.description)
var container = encoder.singleValueContainer()
try container.encode(self.description)
}
}
......
......@@ -8,7 +8,8 @@
import Foundation
public struct HourlyWeather {
public var date: String // since it depends on the local time, it's easier to store it as a string.
public var date: Date
public var timeZone: TimeZone
public var weekDay: WeekDay
public var type: WeatherType = .unknown
......
......@@ -8,7 +8,8 @@
import Foundation
import CoreLocation
public struct Location {
public struct Location: CustomStringConvertible, Equatable {
// MARK: - Data fields
public var coordinates: CLLocation?
public var imageName = "ny_bridge" //we'll possibly need to switch to URL
......@@ -23,4 +24,27 @@ public struct Location {
public var today: CurrentWeather?
public var daily: [DailyWeather] = [DailyWeather]()
public var hourly: [HourlyWeather] = [HourlyWeather]()
// MARK: - Derived fields
public var cityId: String {
return "\(self.countryCode ?? ""):\(self.region ?? ""):\(self.cityName ?? "")"
}
public var description: String {
return "\(cityId) (\(coordinates?.description ?? "no coordinates"))"
}
public static func == (lhs: Location, rhs: Location) -> Bool {
// Should we compare the weather here, too?
// TODO: implement
#warning("Not implemented!")
#if DEBUG
//TODO: REMOVE THIS'
return false
#warning("THIS IS A DEBUG-ONLY PIECE OF CODE! Not even temporary! Remove before making a production build.")
#else
#error("THIS IS A DEBUG-ONLY PIECE OF CODE! Not even temporary! Remove before making a production build.")
#endif
}
}
......@@ -15,10 +15,11 @@ public struct WdtDailySummary: Codable {
public var weekDay: WeekDay
public var weatherCode: WdtWeatherCode?
public var precipitationProbability: Int?
public var sunriseLocal: Time?
public var sunsetLocal: Time?
// Wdt doesn't return local moonrise / moonset... why? :'(
// TODO: store time / date as Date + NSTimeZone
public var sunriseUtc: String?
public var sunsetUtc: String?
public var moonriseUtc: String?
public var moonsetUtc: String?
public var date: String?
enum CodingKeys: String, CodingKey {
case minTempF = "min_temp_F"
......@@ -28,5 +29,10 @@ public struct WdtDailySummary: Codable {
case weekDay = "day_of_week"
case weatherCode = "wx_code"
case precipitationProbability = "pop"
case sunriseUtc = "solunar_sunrise_utc"
case sunsetUtc = "solunar_sunset_utc"
case moonriseUtc = "solunar_moonrise_utc"
case moonsetUtc = "solunar_moonset_utc"
case date = "summary_date"
}
}
//
// WdtHourlySummary.swift
// 1Weather
//
// Created by Demid Merzlyakov on 26.02.2021.
//
import Foundation
struct WdtHourlySummary: Codable {
// TODO: gotta parse date from local + current date at the time of the request.
}
......@@ -16,6 +16,7 @@ public struct WdtSurfaceObservation: Codable {
public var visibilityFt: Double
public var weekDay: WeekDay?
public var weatherCode: WdtWeatherCode?
public var observationLocalTime: String?
enum CodingKeys: String, CodingKey {
case tempF = "temp_F"
......@@ -26,5 +27,6 @@ public struct WdtSurfaceObservation: Codable {
case visibilityFt = "visibility_ft"
case weekDay = "day_of_week_local"
case weatherCode = "wx_code"
case observationLocalTime = "ob_time"
}
}
......@@ -8,7 +8,8 @@
import Foundation
public class WdtWeatherSource: WeatherSource {
static let updateUrl =
static let updateUrlMega = "https://1weather.onelouder.com/feeds/onelouder/mega.php"
static let updateUrlMicro = "https://1weather.onelouder.com/feeds/onelouder2/fm.php"
public func updateWeather(for location: Location, completion: (Location) -> ()) {
......
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