Commit c49077b0 by Demid Merzlyakov

LocationManager: added location list.

parent a7e31619
...@@ -9,6 +9,7 @@ import Foundation ...@@ -9,6 +9,7 @@ import Foundation
public protocol LocationManagerDelegate: class { public protocol LocationManagerDelegate: class {
func locationManager(_ locationManager: LocationManager, changedCurrentLocation newLocation: Location?) func locationManager(_ locationManager: LocationManager, changedCurrentLocation newLocation: Location?)
func locationManager(_ locationManager: LocationManager, updatedLocationsList newList: [Location])
} }
public class LocationManager { public class LocationManager {
...@@ -18,20 +19,36 @@ public class LocationManager { ...@@ -18,20 +19,36 @@ public class LocationManager {
private let defaultLocation = Location(lastTimeUpdated: Date(), private let defaultLocation = Location(lastTimeUpdated: Date(),
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")!)
public private(set) var locations = [Location]() {
didSet {
log.info("Locations list updated: \(locations.map { $0.description }.joined(separator: ", "))")
DispatchQueue.main.async {
self.delegates.invoke { [weak self] (delegate) in
guard let self = self else { return }
delegate.locationManager(self, updatedLocationsList: self.locations)
}
}
}
}
private var _currentLocation: Location? { private var _currentLocation: Location? {
didSet { didSet {
if oldValue?.description != currentLocation?.description { if oldValue?.description != currentLocation?.description {
log.info("Current location changed to: \(currentLocation?.description ?? "nil")") log.info("Current location changed to: \(currentLocation?.description ?? "nil")")
} }
log.info("Location updated.") log.info("Location updated.")
delegates.invoke { [weak self] (delegate) in DispatchQueue.main.async {
guard let self = self else { return } self.delegates.invoke { [weak self] (delegate) in
delegate.locationManager(self, changedCurrentLocation: currentLocation) guard let self = self else { return }
delegate.locationManager(self, changedCurrentLocation: self.currentLocation)
}
} }
} }
} }
public static let shared = LocationManager(weatherUpdateSource: WdtWeatherSource()) public static let shared = LocationManager(weatherUpdateSource: WdtWeatherSource())
public let maxLocationsCount = 12
public init(weatherUpdateSource: WeatherSource) { public init(weatherUpdateSource: WeatherSource) {
self.weatherUpdateSource = weatherUpdateSource self.weatherUpdateSource = weatherUpdateSource
...@@ -40,6 +57,7 @@ public class LocationManager { ...@@ -40,6 +57,7 @@ public class LocationManager {
public var currentLocation: Location? { public var currentLocation: Location? {
get { get {
guard let location = _currentLocation else { guard let location = _currentLocation else {
// TODO: don't do it this way! We won't be able to show search if there's no location!
return defaultLocation return defaultLocation
} }
...@@ -56,7 +74,12 @@ public class LocationManager { ...@@ -56,7 +74,12 @@ public class LocationManager {
log.warning("Update weather: no location.") log.warning("Update weather: no location.")
return return
} }
guard Date().timeIntervalSince(location.lastTimeUpdated) >= weatherUpdateSource.weatherUpdateInterval else {
log.info("Update weather: fresh enough (last updated at \(location.lastTimeUpdated)), skip update.")
return
}
log.info("Update weather for location: \(location)") log.info("Update weather for location: \(location)")
weatherUpdateSource.updateWeather(for: location) { [weak self] (updatedLocation, error) in weatherUpdateSource.updateWeather(for: location) { [weak self] (updatedLocation, error) in
guard let self = self else { return } guard let self = self else { return }
guard let updatedLocation = updatedLocation else { guard let updatedLocation = updatedLocation else {
...@@ -74,6 +97,37 @@ public class LocationManager { ...@@ -74,6 +97,37 @@ public class LocationManager {
} }
} }
} }
// TODO: update weather for all locations
public func add(location: Location) {
if locations.count >= maxLocationsCount {
log.warning("Adding new location, although the location limit is exceeded. New total: \(locations.count + 1)")
// This may happen if a location is added from a push notification.
}
if let existingLocation = locations.first(where: { $0 == location }) {
currentLocation = existingLocation
}
else {
locations.append(location)
currentLocation = location
}
// TODO: we need to update weather for new locations, probably.
// Or not? Should ViewModels handle it?
}
public func remove(location: Location) {
if let index = locations.firstIndex(of: location) {
locations.remove(at: index)
}
else {
log.warning("Couldn't remove \(location), because we couldn't find index.")
}
if currentLocation == location {
currentLocation = locations.first
}
}
// MARK: Delegates management
public func add(delegate: LocationManagerDelegate) { public func add(delegate: LocationManagerDelegate) {
delegates.add(delegate: delegate) delegates.add(delegate: delegate)
......
...@@ -21,6 +21,8 @@ public class WdtWeatherSource: WeatherSource { ...@@ -21,6 +21,8 @@ public class WdtWeatherSource: WeatherSource {
private static let updateUrlMega = "https://1weather.onelouder.com/feeds/onelouder/mega.php" private static let updateUrlMega = "https://1weather.onelouder.com/feeds/onelouder/mega.php"
private static let updateUrlMicro = "https://1weather.onelouder.com/feeds/onelouder2/fm.php" private static let updateUrlMicro = "https://1weather.onelouder.com/feeds/onelouder2/fm.php"
public let weatherUpdateInterval = TimeInterval(15 * 60) // 15 minutes
public func updateWeather(for location: Location, completion: @escaping WeatherSourceCompletion) { public func updateWeather(for location: Location, completion: @escaping WeatherSourceCompletion) {
log.debug("Start update.") log.debug("Start update.")
guard var urlComponents = URLComponents(string: WdtWeatherSource.updateUrlMega) else { guard var urlComponents = URLComponents(string: WdtWeatherSource.updateUrlMega) else {
......
...@@ -10,5 +10,6 @@ import Foundation ...@@ -10,5 +10,6 @@ import Foundation
public typealias WeatherSourceCompletion = (Location?, Error?) -> () public typealias WeatherSourceCompletion = (Location?, Error?) -> ()
public protocol WeatherSource { public protocol WeatherSource {
var weatherUpdateInterval: TimeInterval { get }
func updateWeather(for location: Location, completion: @escaping WeatherSourceCompletion) func updateWeather(for location: Location, completion: @escaping WeatherSourceCompletion)
} }
...@@ -44,4 +44,8 @@ extension TodayViewModel: LocationManagerDelegate { ...@@ -44,4 +44,8 @@ extension TodayViewModel: LocationManagerDelegate {
self.delegate?.viewModelDidChange(model: self) self.delegate?.viewModelDidChange(model: self)
} }
} }
func locationManager(_ locationManager: LocationManager, updatedLocationsList newList: [Location]) {
// do nothing
}
} }
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