From 1d41d8b9b5c9ff47f1e861343c6d453205b64450 Mon Sep 17 00:00:00 2001 From: Pantelis Giazitsis Date: Wed, 29 Oct 2025 15:08:15 +0200 Subject: [PATCH 1/8] Return valid cached forecasts --- .../DomainLayer/UseCases/LocationForecastsUseCase.swift | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/wxm-ios/DomainLayer/DomainLayer/UseCases/LocationForecastsUseCase.swift b/wxm-ios/DomainLayer/DomainLayer/UseCases/LocationForecastsUseCase.swift index 80ec52ef6..edb412d22 100644 --- a/wxm-ios/DomainLayer/DomainLayer/UseCases/LocationForecastsUseCase.swift +++ b/wxm-ios/DomainLayer/DomainLayer/UseCases/LocationForecastsUseCase.swift @@ -49,12 +49,19 @@ public struct LocationForecastsUseCase: LocationForecastsUseCaseApi { public func getForecast(for location: CLLocationCoordinate2D) throws -> AnyPublisher, Never> { if let cachedForecasts: [NetworkDeviceForecastResponse] = cache.getValue(for: location.cacheKey) { + let forecasts = cachedForecasts.filter { forecast in + guard let date = forecast.date.onlyDateStringToDate() else { + return false + } + // Ensure that we return only forecasts later than today + return date.days(from: Date.now) >= 0 + } let response = DataResponse<[NetworkDeviceForecastResponse], NetworkErrorResponse>(request: nil, response: nil, data: nil, metrics: nil, serializationDuration: 0, - result: .success(cachedForecasts)) + result: .success(forecasts)) return Just(response).eraseToAnyPublisher() } From ac26b1c708a13c524f29036fe91a4988abcc8104 Mon Sep 17 00:00:00 2001 From: Pantelis Giazitsis Date: Wed, 29 Oct 2025 15:11:55 +0200 Subject: [PATCH 2/8] Update current location state --- .../UIComponents/Screens/Home/HomeViewModel.swift | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/PresentationLayer/UIComponents/Screens/Home/HomeViewModel.swift b/PresentationLayer/UIComponents/Screens/Home/HomeViewModel.swift index df105118f..1979ef85d 100644 --- a/PresentationLayer/UIComponents/Screens/Home/HomeViewModel.swift +++ b/PresentationLayer/UIComponents/Screens/Home/HomeViewModel.swift @@ -115,7 +115,6 @@ class HomeViewModel: ObservableObject { do { let forecasts = try await fetchForecasts() self.savedLocationsState = forecasts.isEmpty ? .empty : .forecasts(forecasts) - self.currentLocationState = try await getCurrentLocationState() } catch let error as NetworkErrorResponse { let info = error.uiInfo let obj = info.defaultFailObject(type: .home) { [weak self] in @@ -126,9 +125,15 @@ class HomeViewModel: ObservableObject { self.failObj = obj self.isFailed = true + } catch { + print(error) + } + do { + self.currentLocationState = try await getCurrentLocationState() } catch { print(error) + self.currentLocationState = .empty } self.isLoading = false From 96b63092c9a6cd46bfdf5753cddca76a24480fdd Mon Sep 17 00:00:00 2001 From: Pantelis Giazitsis Date: Wed, 29 Oct 2025 15:42:26 +0200 Subject: [PATCH 3/8] Fixed tests --- .../DomainLayer/UseCases/LocationForecastsUseCaseTests.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WeatherXMTests/DomainLayer/UseCases/LocationForecastsUseCaseTests.swift b/WeatherXMTests/DomainLayer/UseCases/LocationForecastsUseCaseTests.swift index 93819f022..50293c0e9 100644 --- a/WeatherXMTests/DomainLayer/UseCases/LocationForecastsUseCaseTests.swift +++ b/WeatherXMTests/DomainLayer/UseCases/LocationForecastsUseCaseTests.swift @@ -42,7 +42,7 @@ struct LocationForecastsUseCaseTests { @Test func getCachedForecast() async throws { let cachedforecast = NetworkDeviceForecastResponse(tz: "123", - date: "", + date: Date.now.getFormattedDate(format: .onlyDate), hourly: [], address: "Address") let location = CLLocationCoordinate2D() From 3e84e7d90883dc123f754ebaa40c23c86f2f05f2 Mon Sep 17 00:00:00 2001 From: Pantelis Giazitsis Date: Wed, 29 Oct 2025 15:48:31 +0200 Subject: [PATCH 4/8] Updated package resolved --- .../project.xcworkspace/xcshareddata/swiftpm/Package.resolved | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wxm-ios.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/wxm-ios.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 7f0388f5d..43adc16f8 100644 --- a/wxm-ios.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/wxm-ios.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -312,8 +312,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-protobuf.git", "state" : { - "revision" : "2547102afd04fe49f1b286090f13ebce07284980", - "version" : "1.31.1" + "revision" : "97bb244f7a575a419ebc8f3c2d33f2feb9c8f7f2", + "version" : "1.33.1" } }, { From 708eb1ef27bc8ef5d1985235227dc0bd0ff35ec2 Mon Sep 17 00:00:00 2001 From: Pantelis Giazitsis Date: Fri, 31 Oct 2025 16:37:10 +0200 Subject: [PATCH 5/8] Handle case of missing account info in keychain --- .../DataLayer/Networking/Interceptors/AuthInterceptor.swift | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/wxm-ios/DataLayer/DataLayer/Networking/Interceptors/AuthInterceptor.swift b/wxm-ios/DataLayer/DataLayer/Networking/Interceptors/AuthInterceptor.swift index 0073178b9..3975fe1e2 100644 --- a/wxm-ios/DataLayer/DataLayer/Networking/Interceptors/AuthInterceptor.swift +++ b/wxm-ios/DataLayer/DataLayer/Networking/Interceptors/AuthInterceptor.swift @@ -87,6 +87,8 @@ class AuthInterceptor: @unchecked Sendable, RequestInterceptor { } }.store(in: &cancellableSet) } catch {} - } + } else { + completion(nil) + } } } From c79d03685732297c381a91fd5d432c01c71bc866 Mon Sep 17 00:00:00 2001 From: Pantelis Giazitsis Date: Fri, 31 Oct 2025 16:37:22 +0200 Subject: [PATCH 6/8] UI fixes --- .../Screens/WeatherStations/MyStations/MyStationsView.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/PresentationLayer/UIComponents/Screens/WeatherStations/MyStations/MyStationsView.swift b/PresentationLayer/UIComponents/Screens/WeatherStations/MyStations/MyStationsView.swift index 6ada87a27..ea3eb42ed 100644 --- a/PresentationLayer/UIComponents/Screens/WeatherStations/MyStations/MyStationsView.swift +++ b/PresentationLayer/UIComponents/Screens/WeatherStations/MyStations/MyStationsView.swift @@ -98,6 +98,7 @@ private struct ContentView: View { .background(Color(colorEnum: .bg)) } + .iPadMaxWidth() } else if devices.isEmpty || !viewModel.isLoggedIn { ZStack { Color(colorEnum: .bg) @@ -111,6 +112,7 @@ private struct ContentView: View { addStationsButton } } + .iPadMaxWidth() } else { weatherStations(devices: devices) .overlay { From 891f58447ee579cc001595769c22b0538ce1cf4a Mon Sep 17 00:00:00 2001 From: Pantelis Giazitsis Date: Fri, 31 Oct 2025 16:39:53 +0200 Subject: [PATCH 7/8] Reset state on login state changes --- .../WeatherStations/MyStations/MyStationsViewModel.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/PresentationLayer/UIComponents/Screens/WeatherStations/MyStations/MyStationsViewModel.swift b/PresentationLayer/UIComponents/Screens/WeatherStations/MyStations/MyStationsViewModel.swift index e93eee11a..dc3e11e3c 100644 --- a/PresentationLayer/UIComponents/Screens/WeatherStations/MyStations/MyStationsViewModel.swift +++ b/PresentationLayer/UIComponents/Screens/WeatherStations/MyStations/MyStationsViewModel.swift @@ -80,6 +80,8 @@ public final class MyStationsViewModel: ObservableObject { mainVM?.$isUserLoggedIn.sink { [weak self] value in self?.isLoggedIn = value + self?.isFailed = false + self?.failObj = nil }.store(in: &cancellableSet) observeFilters() From c0323a082cc68621faf8b8de1c0a2a9e4707de36 Mon Sep 17 00:00:00 2001 From: Pantelis Giazitsis Date: Mon, 3 Nov 2025 14:01:42 +0200 Subject: [PATCH 8/8] Fixed cache interval for the location forecasts --- .../DomainLayer/UseCases/LocationForecastsUseCase.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wxm-ios/DomainLayer/DomainLayer/UseCases/LocationForecastsUseCase.swift b/wxm-ios/DomainLayer/DomainLayer/UseCases/LocationForecastsUseCase.swift index edb412d22..bbdfc83f2 100644 --- a/wxm-ios/DomainLayer/DomainLayer/UseCases/LocationForecastsUseCase.swift +++ b/wxm-ios/DomainLayer/DomainLayer/UseCases/LocationForecastsUseCase.swift @@ -18,7 +18,7 @@ public struct LocationForecastsUseCase: LocationForecastsUseCaseApi { nonisolated(unsafe) private let keychainRepository: KeychainRepository private let savedLocationsUDKey = UserDefaults.GenericKey.savedLocations.rawValue private let forecastsCacheKey = UserDefaults.GenericKey.savedForecasts.rawValue - private let cacheInterval: TimeInterval = 15 * 60 * 60 + private let cacheInterval: TimeInterval = 15 * 60 // 15 mins nonisolated(unsafe) private let notificationsPublisher: NotificationCenter.Publisher = NotificationCenter.default.publisher(for: .savedLocationsUpdated) nonisolated(unsafe) let cache: TimeValidationCache<[NetworkDeviceForecastResponse]> public var locationAuthorization: WXMLocationManager.Status {