From c350dba5e57faa057788b582bd74b126eeca5c17 Mon Sep 17 00:00:00 2001 From: Inder Dhir Date: Fri, 13 Jun 2025 20:14:49 -0400 Subject: [PATCH 1/4] Fix an issue with continuation --- .../ViewModel/Repository/System/SystemLocationFetcher.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DatWeatherDoe/ViewModel/Repository/System/SystemLocationFetcher.swift b/DatWeatherDoe/ViewModel/Repository/System/SystemLocationFetcher.swift index 4ed3720..9d6ad8f 100644 --- a/DatWeatherDoe/ViewModel/Repository/System/SystemLocationFetcher.swift +++ b/DatWeatherDoe/ViewModel/Repository/System/SystemLocationFetcher.swift @@ -134,8 +134,8 @@ extension SystemLocationFetcher: CLLocationManagerDelegate { nonisolated func locationManager(_ manager: CLLocationManager, didUpdateLocations _: [CLLocation]) { let coordinate = manager.location?.coordinate ?? .init(latitude: .zero, longitude: .zero) Task(priority: .high) { - await locationUpdateContinuation?.resume(returning: coordinate) await updateCachedLocation(coordinate) + await locationUpdateContinuation?.resume(returning: coordinate) } } } From 4b50d836e73a13f78aef1b697d735cc6e4bc0aa2 Mon Sep 17 00:00:00 2001 From: Inder Dhir Date: Fri, 13 Jun 2025 20:29:10 -0400 Subject: [PATCH 2/4] Fix issue with lat/long not saving --- DatWeatherDoe/DatWeatherDoeApp.swift | 15 ++++++++++++--- .../UI/Configure/ConfigureViewModel.swift | 3 ++- DatWeatherDoe/UI/Menu Bar/MenuView.swift | 10 ++++------ 3 files changed, 18 insertions(+), 10 deletions(-) diff --git a/DatWeatherDoe/DatWeatherDoeApp.swift b/DatWeatherDoe/DatWeatherDoeApp.swift index 2cf1ee7..18b07fa 100644 --- a/DatWeatherDoe/DatWeatherDoeApp.swift +++ b/DatWeatherDoe/DatWeatherDoeApp.swift @@ -13,8 +13,9 @@ import SwiftUI @main struct DatWeatherDoeApp: App { @State private var configManager: ConfigManager + @ObservedObject private var configureViewModel = ConfigureViewModel(configManager: ConfigManager()) @ObservedObject private var viewModel: WeatherViewModel - @State private var isMenuPresented: Bool = false + @State private var isMenuPresented = false @State private var statusItem: NSStatusItem? init() { @@ -39,6 +40,7 @@ struct DatWeatherDoeApp: App { content: { MenuView( viewModel: viewModel, + configureViewModel: configureViewModel, onSeeWeather: { viewModel.seeForecastForCurrentCity() closePopover() @@ -48,7 +50,6 @@ struct DatWeatherDoeApp: App { closePopover() }, onSave: { - viewModel.getUpdatedWeatherAfterRefresh() closePopover() } ) @@ -61,7 +62,15 @@ struct DatWeatherDoeApp: App { } ) .menuBarExtraAccess(isPresented: $isMenuPresented) { statusItem in - self.statusItem = statusItem + Task { @MainActor in + self.statusItem = statusItem + } + } + .onChange(of: isMenuPresented) { newValue in + if !newValue { + configureViewModel.saveConfig() + viewModel.getUpdatedWeatherAfterRefresh() + } } .windowStyle(.hiddenTitleBar) .menuBarExtraStyle(.window) diff --git a/DatWeatherDoe/UI/Configure/ConfigureViewModel.swift b/DatWeatherDoe/UI/Configure/ConfigureViewModel.swift index 84f70eb..aaea19a 100644 --- a/DatWeatherDoe/UI/Configure/ConfigureViewModel.swift +++ b/DatWeatherDoe/UI/Configure/ConfigureViewModel.swift @@ -18,7 +18,7 @@ final class ConfigureViewModel: ObservableObject { didSet { configManager.weatherSource = weatherSource.rawValue } } - @Published var weatherSourceText = "" { + @Published var weatherSourceText: String { didSet { configManager.weatherSourceText = weatherSourceText } } @@ -69,6 +69,7 @@ final class ConfigureViewModel: ObservableObject { measurementUnit = configManager.parsedMeasurementUnit weatherSource = WeatherSource(rawValue: configManager.weatherSource) ?? .location + weatherSourceText = configManager.weatherSourceText switch configManager.refreshInterval { case 300: refreshInterval = .fiveMinutes diff --git a/DatWeatherDoe/UI/Menu Bar/MenuView.swift b/DatWeatherDoe/UI/Menu Bar/MenuView.swift index 2abcc28..96ab133 100644 --- a/DatWeatherDoe/UI/Menu Bar/MenuView.swift +++ b/DatWeatherDoe/UI/Menu Bar/MenuView.swift @@ -10,8 +10,8 @@ import SwiftUI struct MenuView: View { @ObservedObject var viewModel: WeatherViewModel + @ObservedObject var configureViewModel: ConfigureViewModel private var menuOptionData: MenuOptionData? - @State private var configureViewModel: ConfigureViewModel private var version: String private var onSeeWeather: () -> Void private var onRefresh: () -> Void @@ -19,16 +19,17 @@ struct MenuView: View { init( viewModel: WeatherViewModel, + configureViewModel: ConfigureViewModel, onSeeWeather: @escaping () -> Void, onRefresh: @escaping () -> Void, onSave: @escaping () -> Void ) { self.viewModel = viewModel + self.configureViewModel = configureViewModel self.onSeeWeather = onSeeWeather self.onRefresh = onRefresh self.onSave = onSave - configureViewModel = ConfigureViewModel(configManager: ConfigManager()) version = (Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String) ?? "1.0.0" } @@ -45,10 +46,7 @@ struct MenuView: View { ConfigureView( viewModel: configureViewModel, version: version, - onSave: { - configureViewModel.saveConfig() - onSave() - }, + onSave: onSave, onQuit: { NSApp.terminate(self) } From 3d1945160506c460190d24662e71ac605a6f24af Mon Sep 17 00:00:00 2001 From: Inder Dhir Date: Fri, 13 Jun 2025 20:29:31 -0400 Subject: [PATCH 3/4] Bump up version to 5.4.0 --- DatWeatherDoe.xcodeproj/project.pbxproj | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/DatWeatherDoe.xcodeproj/project.pbxproj b/DatWeatherDoe.xcodeproj/project.pbxproj index 4677799..09e98a4 100644 --- a/DatWeatherDoe.xcodeproj/project.pbxproj +++ b/DatWeatherDoe.xcodeproj/project.pbxproj @@ -867,7 +867,7 @@ CODE_SIGN_IDENTITY = "Mac Developer"; "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Mac Developer"; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 51; + CURRENT_PROJECT_VERSION = 52; DEAD_CODE_STRIPPING = YES; DEVELOPMENT_ASSET_PATHS = "\"DatWeatherDoe/Resources/DevelopmentAssets\" \"DatWeatherDoe/Resources/DevelopmentAssets/TestData.swift\""; DEVELOPMENT_TEAM = Q8X4D3A8MT; @@ -879,7 +879,7 @@ "$(inherited)", "@executable_path/../Frameworks", ); - MARKETING_VERSION = 5.3.1; + MARKETING_VERSION = 5.4.0; PRODUCT_BUNDLE_IDENTIFIER = com.inderdhir.DatWeatherDoe.debug; PRODUCT_NAME = DatWeatherDoe; PROVISIONING_PROFILE = ""; @@ -896,7 +896,7 @@ CODE_SIGN_IDENTITY = "Mac Developer"; "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Mac Developer"; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 51; + CURRENT_PROJECT_VERSION = 52; DEAD_CODE_STRIPPING = YES; DEVELOPMENT_ASSET_PATHS = "\"DatWeatherDoe/Resources/DevelopmentAssets\" \"DatWeatherDoe/Resources/DevelopmentAssets/TestData.swift\""; DEVELOPMENT_TEAM = Q8X4D3A8MT; @@ -908,7 +908,7 @@ "$(inherited)", "@executable_path/../Frameworks", ); - MARKETING_VERSION = 5.3.1; + MARKETING_VERSION = 5.4.0; PRODUCT_BUNDLE_IDENTIFIER = com.inderdhir.DatWeatherDoe; PRODUCT_NAME = DatWeatherDoe; PROVISIONING_PROFILE = ""; From aae2663fe29b20a928d113d7cb89f8f988aa1566 Mon Sep 17 00:00:00 2001 From: Inder Dhir Date: Fri, 13 Jun 2025 20:41:10 -0400 Subject: [PATCH 4/4] Fix lint issues --- .../Zip Code/ZipCodeValidator.swift | 24 ------ .../Zip Code/ZipCodeWeatherRepository.swift | 79 ------------------- .../Zip Code/ZipCodeWeatherURLBuilder.swift | 32 -------- DatWeatherDoe/API/Response/ForecastData.swift | 1 + .../API/Response/WeatherAPIResponse.swift | 3 +- DatWeatherDoe/DatWeatherDoeApp.swift | 2 +- .../DevelopmentAssets/TestData.swift | 1 + .../TemperatureForecastTextBuilder.swift | 6 +- DatWeatherDoe/UI/Menu Bar/CustomButton.swift | 7 +- .../UI/Menu Bar/MenuOptionsView.swift | 5 +- .../ViewModel/WeatherViewModel.swift | 5 +- 11 files changed, 23 insertions(+), 142 deletions(-) delete mode 100644 DatWeatherDoe/API/Repository/Zip Code/ZipCodeValidator.swift delete mode 100644 DatWeatherDoe/API/Repository/Zip Code/ZipCodeWeatherRepository.swift delete mode 100644 DatWeatherDoe/API/Repository/Zip Code/ZipCodeWeatherURLBuilder.swift diff --git a/DatWeatherDoe/API/Repository/Zip Code/ZipCodeValidator.swift b/DatWeatherDoe/API/Repository/Zip Code/ZipCodeValidator.swift deleted file mode 100644 index dcc627b..0000000 --- a/DatWeatherDoe/API/Repository/Zip Code/ZipCodeValidator.swift +++ /dev/null @@ -1,24 +0,0 @@ -// -// ZipCodeValidator.swift -// DatWeatherDoe -// -// Created by Inder Dhir on 1/13/22. -// Copyright © 2022 Inder Dhir. All rights reserved. -// - -final class ZipCodeValidator: WeatherValidatorType { - private let zipCode: String - - init(zipCode: String) { - self.zipCode = zipCode - } - - func validate() throws { - let isZipPresent = !zipCode.isEmpty - let isZipPresentWithCountryCode = zipCode.split(separator: ",").count == 2 - let isValid = isZipPresent && isZipPresentWithCountryCode - if !isValid { - throw WeatherError.zipCodeIncorrect - } - } -} diff --git a/DatWeatherDoe/API/Repository/Zip Code/ZipCodeWeatherRepository.swift b/DatWeatherDoe/API/Repository/Zip Code/ZipCodeWeatherRepository.swift deleted file mode 100644 index fc15a4c..0000000 --- a/DatWeatherDoe/API/Repository/Zip Code/ZipCodeWeatherRepository.swift +++ /dev/null @@ -1,79 +0,0 @@ -// -// ZipCodeWeatherRepository.swift -// DatWeatherDoe -// -// Created by Inder Dhir on 1/14/22. -// Copyright © 2022 Inder Dhir. All rights reserved. -// - -import Foundation - -final class ZipCodeWeatherRepository: WeatherRepositoryType { - private let appId: String - private let zipCode: String - private let networkClient: NetworkClient - private let logger: DatWeatherDoeLoggerType - - init( - appId: String, - zipCode: String, - networkClient: NetworkClient, - logger: DatWeatherDoeLoggerType - ) { - self.appId = appId - self.zipCode = zipCode - self.networkClient = networkClient - self.logger = logger - } - - func getWeather(completion: @escaping (Result) -> Void) { - logger.debug("Getting weather via zip code") - - do { - try validateZipCode() - let url = try buildURL() - performRequest(url: url, completion: { [weak self] result in - self?.parseNetworkResult(result: result, completion: completion) - }) - } catch { - logger.error("Getting weather via zip code failed. Zip code is incorrect!") - - completion(.failure(error)) - } - } - - private func validateZipCode() throws { - try ZipCodeValidator(zipCode: zipCode).validate() - } - - private func buildURL() throws -> URL { - try ZipCodeWeatherURLBuilder(appId: appId, zipCode: zipCode).build() - } - - private func performRequest(url: URL, completion: @escaping (Result) -> Void) { - networkClient.performRequest(url: url, completion: completion) - } - - private func parseNetworkResult( - result: Result, - completion: @escaping (Result) -> Void - ) { - switch result { - case let .success(data): - do { - let weatherData = try parseWeatherData(data) - completion(.success(weatherData)) - } catch { - let weatherError = (error as? WeatherError) ?? WeatherError.other - completion(.failure(weatherError)) - } - case let .failure(error): - let weatherError = (error as? WeatherError) ?? WeatherError.other - completion(.failure(weatherError)) - } - } - - private func parseWeatherData(_ data: Data) throws -> WeatherAPIResponse { - try WeatherAPIResponseParser().parse(data) - } -} diff --git a/DatWeatherDoe/API/Repository/Zip Code/ZipCodeWeatherURLBuilder.swift b/DatWeatherDoe/API/Repository/Zip Code/ZipCodeWeatherURLBuilder.swift deleted file mode 100644 index 8a0472d..0000000 --- a/DatWeatherDoe/API/Repository/Zip Code/ZipCodeWeatherURLBuilder.swift +++ /dev/null @@ -1,32 +0,0 @@ -// -// ZipCodeWeatherURLBuilder.swift -// DatWeatherDoe -// -// Created by Inder Dhir on 1/16/22. -// Copyright © 2022 Inder Dhir. All rights reserved. -// - -import Foundation - -final class ZipCodeWeatherURLBuilder: WeatherURLBuilder { - private let zipCode: String - - init(appId: String, zipCode: String) { - self.zipCode = zipCode - super.init(appId: appId) - } - - override func build() throws -> URL { - let queryItems: [URLQueryItem] = [ - URLQueryItem(name: "appid", value: appId), - URLQueryItem(name: "zip", value: zipCode), - ] - - var urlComps = URLComponents(string: apiUrlString) - urlComps?.queryItems = queryItems - guard let finalUrl = urlComps?.url else { - throw WeatherError.unableToConstructUrl - } - return finalUrl - } -} diff --git a/DatWeatherDoe/API/Response/ForecastData.swift b/DatWeatherDoe/API/Response/ForecastData.swift index 706cd20..410de1b 100644 --- a/DatWeatherDoe/API/Response/ForecastData.swift +++ b/DatWeatherDoe/API/Response/ForecastData.swift @@ -29,5 +29,6 @@ struct ForecastDayData: Decodable { } struct HourlyUVIndex: Decodable { + // swiftlint:disable:next identifier_name let uv: Double } diff --git a/DatWeatherDoe/API/Response/WeatherAPIResponse.swift b/DatWeatherDoe/API/Response/WeatherAPIResponse.swift index 64217e5..1eb1e0a 100644 --- a/DatWeatherDoe/API/Response/WeatherAPIResponse.swift +++ b/DatWeatherDoe/API/Response/WeatherAPIResponse.swift @@ -84,7 +84,8 @@ struct WeatherAPIResponse: Decodable { ) } - let airQualityContainer = try currentContainer.nestedContainer(keyedBy: AirQualityKeys.self, forKey: .airQuality) + let airQualityContainer = + try currentContainer.nestedContainer(keyedBy: AirQualityKeys.self, forKey: .airQuality) airQualityIndex = try airQualityContainer.decode(AirQualityIndex.self, forKey: .usEpaIndex) } diff --git a/DatWeatherDoe/DatWeatherDoeApp.swift b/DatWeatherDoe/DatWeatherDoeApp.swift index 18b07fa..50055c3 100644 --- a/DatWeatherDoe/DatWeatherDoeApp.swift +++ b/DatWeatherDoe/DatWeatherDoeApp.swift @@ -67,7 +67,7 @@ struct DatWeatherDoeApp: App { } } .onChange(of: isMenuPresented) { newValue in - if !newValue { + if !newValue { configureViewModel.saveConfig() viewModel.getUpdatedWeatherAfterRefresh() } diff --git a/DatWeatherDoe/Resources/DevelopmentAssets/TestData.swift b/DatWeatherDoe/Resources/DevelopmentAssets/TestData.swift index 137ead7..ca7d44d 100644 --- a/DatWeatherDoe/Resources/DevelopmentAssets/TestData.swift +++ b/DatWeatherDoe/Resources/DevelopmentAssets/TestData.swift @@ -10,6 +10,7 @@ import Foundation let uvIndicesFor24Hours: [HourlyUVIndex] = { var arr: [HourlyUVIndex] = [] + // swiftlint:disable:next identifier_name for i in 1 ... 24 { arr.append(HourlyUVIndex(uv: 0)) } diff --git a/DatWeatherDoe/UI/Decorator/Text/Temperature/TemperatureForecastTextBuilder.swift b/DatWeatherDoe/UI/Decorator/Text/Temperature/TemperatureForecastTextBuilder.swift index efafac5..5ecd18d 100644 --- a/DatWeatherDoe/UI/Decorator/Text/Temperature/TemperatureForecastTextBuilder.swift +++ b/DatWeatherDoe/UI/Decorator/Text/Temperature/TemperatureForecastTextBuilder.swift @@ -84,13 +84,15 @@ final class TemperatureForecastTextBuilder: TemperatureForecastTextBuilderType { } private func buildTemperatureForUnit(_ unit: TemperatureUnit) -> String { - let maxTemp = unit == .fahrenheit ? forecastTemperatureData.maxTempF : forecastTemperatureData.maxTempC + let maxTemp = unit == .fahrenheit ? + forecastTemperatureData.maxTempF : forecastTemperatureData.maxTempC let formattedMaxTemp = buildFormattedTemperature(maxTemp, unit: unit) let maxTempStr = [upArrowStr, formattedMaxTemp] .compactMap { $0 } .joined() - let minTemp = unit == .fahrenheit ? forecastTemperatureData.minTempF : forecastTemperatureData.minTempC + let minTemp = unit == .fahrenheit ? + forecastTemperatureData.minTempF : forecastTemperatureData.minTempC let formatedMinTemp = buildFormattedTemperature(minTemp, unit: unit) let minTempStr = [downArrowStr, formatedMinTemp] .compactMap { $0 } diff --git a/DatWeatherDoe/UI/Menu Bar/CustomButton.swift b/DatWeatherDoe/UI/Menu Bar/CustomButton.swift index 2868d39..d1ae7d8 100644 --- a/DatWeatherDoe/UI/Menu Bar/CustomButton.swift +++ b/DatWeatherDoe/UI/Menu Bar/CustomButton.swift @@ -14,7 +14,12 @@ struct CustomButton: View { let shortcutKey: KeyEquivalent let onClick: () -> Void - init(text: LocalizedStringKey, textColor: Color = Color.primary, shortcutKey: KeyEquivalent, onClick: @escaping () -> Void) { + init( + text: LocalizedStringKey, + textColor: Color = Color.primary, + shortcutKey: KeyEquivalent, + onClick: @escaping () -> Void + ) { self.text = text self.textColor = textColor self.shortcutKey = shortcutKey diff --git a/DatWeatherDoe/UI/Menu Bar/MenuOptionsView.swift b/DatWeatherDoe/UI/Menu Bar/MenuOptionsView.swift index 94f5605..579a543 100644 --- a/DatWeatherDoe/UI/Menu Bar/MenuOptionsView.swift +++ b/DatWeatherDoe/UI/Menu Bar/MenuOptionsView.swift @@ -28,7 +28,10 @@ struct MenuOptionsView: View { NonInteractiveMenuOptionView(icon: .thermometer, text: data?.weatherText) NonInteractiveMenuOptionView(icon: .sun, text: data?.sunriseSunsetText) NonInteractiveMenuOptionView(icon: .wind, text: data?.tempHumidityWindText) - NonInteractiveMenuOptionView(icon: .uvIndexAndAirQualityText, text: data?.uvIndexAndAirQualityText) + NonInteractiveMenuOptionView( + icon: .uvIndexAndAirQualityText, + text: data?.uvIndexAndAirQualityText + ) } HStack { diff --git a/DatWeatherDoe/ViewModel/WeatherViewModel.swift b/DatWeatherDoe/ViewModel/WeatherViewModel.swift index 3bb32e9..76e286b 100644 --- a/DatWeatherDoe/ViewModel/WeatherViewModel.swift +++ b/DatWeatherDoe/ViewModel/WeatherViewModel.swift @@ -141,7 +141,10 @@ final class WeatherViewModel: WeatherViewModelType, ObservableObject { ) } - private func getWeather(repository: WeatherRepositoryType, unit: MeasurementUnit) async throws -> WeatherData { + private func getWeather( + repository: WeatherRepositoryType, + unit: MeasurementUnit + ) async throws -> WeatherData { let repository = repository let responseTask = Task {