Commit 969bf37f by Demid Merzlyakov

Search and location selection working. TODO: location deletion.

parent 355b39f0
......@@ -43,5 +43,7 @@
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>NSLocationWhenInUseUsageDescription</key>
<string>1Weather uses your location to provide you with weather forecasts and ads. For more info visit 1weatherapp.com/privacy</string>
</dict>
</plist>
......@@ -183,6 +183,9 @@ internal class DeviceLocationMonitor: NSObject {
// MARK: - CLLocationManagerDelegate
extension DeviceLocationMonitor: CLLocationManagerDelegate {
internal func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
guard status != .notDetermined else {
return
}
switch status {
case .denied:
analytics(log: .ANALYTICS_LOC_PERM_NO)
......
......@@ -38,7 +38,11 @@ public class LocationManager {
public private(set) var locations = [Location]() {
didSet {
log.info("Locations list updated: \(locations.map { $0.description }.joined(separator: ", "))")
let newValue = locations
DispatchQueue.main.async {
if let selectedIndex = self.selectedLocationIndex, selectedIndex >= newValue.count {
self.selectedLocationIndex = newValue.count > 0 ? 0 : nil
}
self.delegates.invoke { [weak self] (delegate) in
guard let self = self else { return }
delegate.locationManager(self, updatedLocationsList: self.locations)
......@@ -47,17 +51,64 @@ public class LocationManager {
}
}
private var _selectedLocation: Location? {
private var selectedLocationIndex: Int? {
didSet {
if oldValue?.description != selectedLocation?.description {
guard selectedLocationIndex != oldValue else {
return
}
var oldLocation: Location?
if let oldValue = oldValue, oldValue < locations.count {
oldLocation = locations[oldValue]
}
var newLocation: Location?
if let newValue = selectedLocationIndex, newValue < locations.count {
newLocation = locations[newValue]
}
if oldLocation?.description != newLocation?.description {
log.info("Current location changed to: \(selectedLocation?.description ?? "nil")")
}
log.info("Location updated.")
DispatchQueue.main.async {
self.delegates.invoke { [weak self] (delegate) in
guard let self = self else { return }
delegate.locationManager(self, changedSelectedLocation: self.selectedLocation)
delegate.locationManager(self, changedSelectedLocation: newLocation)
}
}
self.updateWeather(for: newLocation)
}
}
public var selectedLocation: Location? {
get {
guard let index = selectedLocationIndex else {
// TODO: don't do it this way, because we won't be able to tell that no location is currently selected!
return defaultLocation
}
guard index < locations.count else {
assertionFailure("This shouldn't happen. Got to investigate.")
// But in runtime we can handle it gracefully
DispatchQueue.main.async {
self.selectedLocationIndex = self.locations.count > 0 ? 0 : nil
}
return nil
}
return locations[index]
}
set {
guard let location = newValue else {
self.selectedLocationIndex = nil
return
}
if let index = locations.firstIndex(of: location) {
self.selectedLocationIndex = index
locations[index] = location
self.delegates.invoke { [weak self] (delegate) in
guard let self = self else { return }
delegate.locationManager(self, changedSelectedLocation: location)
}
}
else {
self.addIfNeeded(location: location, selectLocation: true)
}
}
}
......@@ -65,39 +116,24 @@ public class LocationManager {
public static let shared = LocationManager(weatherUpdateSource: WdtWeatherSource())
public let maxLocationsCount = 12
public init(weatherUpdateSource: WeatherSource) {
self.weatherUpdateSource = weatherUpdateSource
self.deviceLocationMonitor = DeviceLocationMonitor()
self.deviceLocationMonitor.delegate = self
}
public var selectedLocation: Location? {
get {
guard let location = _selectedLocation else {
// TODO: don't do it this way! We won't be able to show search if there's no location!
return defaultLocation
}
return location
}
set {
_selectedLocation = newValue
}
}
public func updateWeather() {
guard let location = selectedLocation else {
log.warning("Update weather: no location.")
public func updateWeather(for location: Location?) {
guard let location = location else {
log.warning("Update weather: empty location.")
return
}
if let lastTimeUpdated = location.lastWeatherUpdateDate {
guard Date().timeIntervalSince(lastTimeUpdated) >= weatherUpdateSource.weatherUpdateInterval else {
log.info("Update weather: fresh enough (last updated at \(location.lastWeatherUpdateDate)), skip update.")
log.info("Update weather: fresh enough (last updated at \(lastTimeUpdated)), skip update.")
return
}
}
log.info("Update weather for location: \(location)")
weatherUpdateSource.updateWeather(for: location) { [weak self] (updatedLocation, error) in
......@@ -135,22 +171,20 @@ public class LocationManager {
locations = [location] + locations
}
if selectLocation {
selectedLocation = location
selectedLocationIndex = 0
}
}
else if let existingLocation = locations.first(where: { $0 == location }) {
else if let existingLocationIndex = locations.firstIndex(where: { $0 == location }) {
if selectLocation {
selectedLocation = existingLocation
selectedLocationIndex = existingLocationIndex
}
}
else {
locations.append(location)
if selectLocation {
selectedLocation = location
selectedLocationIndex = locations.count - 1
}
}
// TODO: we need to update weather for new locations, probably.
// Or not? Should ViewModels handle it?
}
public func addIfNeeded(partialLocation: PartialLocation, selectLocation: Bool) {
......
......@@ -442,6 +442,8 @@ extension CitiesViewController: UITableViewDelegate {
analytics(log: .ANALYTICS_FTUE_SEARCH_POPULAR)
}
}
self.close()
//TODO: should be done from ViewModel?
}
}
......
......@@ -191,6 +191,8 @@ public class LocationsViewModel {
return
}
locationManager.addIfNeeded(partialLocation: city, selectLocation: true)
// TODO: close View Controller here, otherwise we can't choose already selected city.
// Currently it's done in the didSelectRow method in the VC.
}
func delete(city: PartialLocation) {
......
......@@ -50,7 +50,8 @@ class TodayViewController: UIViewController {
}
@objc private func handleCityButton() {
print("Handle city button")
let locationViewController = LocationViewController(closeButtonIsHidden: false)
present(locationViewController, animated: true)
}
@objc private func handleNotificationButton() {
......
......@@ -31,7 +31,7 @@ class ForecastViewModel: ViewModelProtocol {
}
public func updateWeather() {
locationManager.updateWeather()
locationManager.updateWeather(for: locationManager.selectedLocation)
}
}
......
......@@ -31,7 +31,7 @@ class TodayViewModel: ViewModelProtocol {
}
public func updateWeather() {
locationManager.updateWeather()
locationManager.updateWeather(for: locationManager.selectedLocation)
}
}
......
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