From 6a5eaed1cfb2dcfd304c1df7ee3aa2a3264e2182 Mon Sep 17 00:00:00 2001 From: Inder Dhir Date: Mon, 13 Oct 2025 22:06:43 -0400 Subject: [PATCH 1/5] Enable approachable concurrency --- DatWeatherDoe.xcodeproj/project.pbxproj | 4 +++ DatWeatherDoe/API/NetworkClient.swift | 2 +- DatWeatherDoe/DatWeatherDoeApp.swift | 4 +-- .../System/SystemLocationFetcher.swift | 27 +++++++++---------- .../ViewModel/WeatherViewModel.swift | 20 +++----------- .../ViewModel/WeatherViewModelType.swift | 1 - 6 files changed, 21 insertions(+), 37 deletions(-) diff --git a/DatWeatherDoe.xcodeproj/project.pbxproj b/DatWeatherDoe.xcodeproj/project.pbxproj index 16664b7..3130153 100644 --- a/DatWeatherDoe.xcodeproj/project.pbxproj +++ b/DatWeatherDoe.xcodeproj/project.pbxproj @@ -876,6 +876,8 @@ PRODUCT_BUNDLE_IDENTIFIER = com.inderdhir.DatWeatherDoe.debug; PRODUCT_NAME = DatWeatherDoe; PROVISIONING_PROFILE = ""; + SWIFT_APPROACHABLE_CONCURRENCY = YES; + SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor; SWIFT_STRICT_CONCURRENCY = complete; SWIFT_SWIFT3_OBJC_INFERENCE = Default; }; @@ -908,6 +910,8 @@ PRODUCT_BUNDLE_IDENTIFIER = com.inderdhir.DatWeatherDoe; PRODUCT_NAME = DatWeatherDoe; PROVISIONING_PROFILE = ""; + SWIFT_APPROACHABLE_CONCURRENCY = YES; + SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor; SWIFT_STRICT_CONCURRENCY = complete; SWIFT_SWIFT3_OBJC_INFERENCE = Default; }; diff --git a/DatWeatherDoe/API/NetworkClient.swift b/DatWeatherDoe/API/NetworkClient.swift index 13df0e5..929f4e3 100644 --- a/DatWeatherDoe/API/NetworkClient.swift +++ b/DatWeatherDoe/API/NetworkClient.swift @@ -12,7 +12,7 @@ protocol NetworkClientType { func performRequest(url: URL) async throws -> Data } -final actor NetworkClient: NetworkClientType { +final class NetworkClient: NetworkClientType { func performRequest(url: URL) async throws -> Data { do { return try await URLSession.shared.data(from: url).0 diff --git a/DatWeatherDoe/DatWeatherDoeApp.swift b/DatWeatherDoe/DatWeatherDoeApp.swift index fd03ea3..a7a245d 100644 --- a/DatWeatherDoe/DatWeatherDoeApp.swift +++ b/DatWeatherDoe/DatWeatherDoeApp.swift @@ -62,9 +62,7 @@ struct DatWeatherDoeApp: App { } ) .menuBarExtraAccess(isPresented: $isMenuPresented) { statusItem in - Task { @MainActor in - self.statusItem = statusItem - } + self.statusItem = statusItem } .onChange(of: isMenuPresented) { newValue in if !newValue { diff --git a/DatWeatherDoe/ViewModel/Repository/System/SystemLocationFetcher.swift b/DatWeatherDoe/ViewModel/Repository/System/SystemLocationFetcher.swift index 9d6ad8f..9c06838 100644 --- a/DatWeatherDoe/ViewModel/Repository/System/SystemLocationFetcher.swift +++ b/DatWeatherDoe/ViewModel/Repository/System/SystemLocationFetcher.swift @@ -44,14 +44,12 @@ final actor SystemLocationFetcher: NSObject, SystemLocationFetcherType { switch CLLocationManager().authorizationStatus { case .notDetermined: let isAuthorized = await withCheckedContinuation { continuation in - Task(priority: .high) { - logger.debug("Location permission not determined") + logger.debug("Location permission not determined") - permissionContinuation = continuation + permissionContinuation = continuation - await MainActor.run { - locationManager.requestWhenInUseAuthorization() - } + Task { @MainActor in + locationManager.requestWhenInUseAuthorization() } } @@ -86,12 +84,10 @@ final actor SystemLocationFetcher: NSObject, SystemLocationFetcherType { do { let latestLocation = try await withCheckedThrowingContinuation { continuation in - Task(priority: .high) { - locationUpdateContinuation = continuation - - await MainActor.run { - locationManager.startUpdatingLocation() - } + locationUpdateContinuation = continuation + + Task { @MainActor in + locationManager.startUpdatingLocation() } } @@ -120,22 +116,23 @@ extension SystemLocationFetcher: CLLocationManagerDelegate { nonisolated func locationManagerDidChangeAuthorization(_: CLLocationManager) { let isAuthorized = CLLocationManager().authorizationStatus == .authorized - Task(priority: .high) { + Task { await permissionContinuation?.resume(returning: isAuthorized) } } nonisolated func locationManager(_: CLLocationManager, didFailWithError error: Error) { - Task(priority: .high) { + Task { await locationUpdateContinuation?.resume(throwing: error) } } nonisolated func locationManager(_ manager: CLLocationManager, didUpdateLocations _: [CLLocation]) { let coordinate = manager.location?.coordinate ?? .init(latitude: .zero, longitude: .zero) - Task(priority: .high) { + Task { await updateCachedLocation(coordinate) await locationUpdateContinuation?.resume(returning: coordinate) } } } + diff --git a/DatWeatherDoe/ViewModel/WeatherViewModel.swift b/DatWeatherDoe/ViewModel/WeatherViewModel.swift index 76e286b..450e079 100644 --- a/DatWeatherDoe/ViewModel/WeatherViewModel.swift +++ b/DatWeatherDoe/ViewModel/WeatherViewModel.swift @@ -9,7 +9,6 @@ import Foundation import OSLog -@MainActor final class WeatherViewModel: WeatherViewModelType, ObservableObject { private let locationFetcher: SystemLocationFetcherType private var weatherFactory: WeatherRepositoryFactoryType @@ -39,9 +38,7 @@ final class WeatherViewModel: WeatherViewModelType, ObservableObject { } deinit { - Task { [weatherTimerTask] in - weatherTimerTask?.cancel() - } + weatherTimerTask?.cancel() } func setup(with formatter: WeatherDataFormatter) { @@ -91,12 +88,8 @@ final class WeatherViewModel: WeatherViewModelType, ObservableObject { private func getWeatherAfterUpdatingLocation() async throws -> WeatherData { let locationFetcher = locationFetcher - let locationTask = Task { - let location = try await locationFetcher.getLocation() - return location - } + let location = try await locationFetcher.getLocation() - let location = try await locationTask.value return try await getWeather( repository: weatherFactory.create(location: location), unit: configManager.parsedMeasurementUnit @@ -145,15 +138,8 @@ final class WeatherViewModel: WeatherViewModelType, ObservableObject { repository: WeatherRepositoryType, unit: MeasurementUnit ) async throws -> WeatherData { - let repository = repository - - let responseTask = Task { - let response = try await repository.getWeather() - return response - } - do { - let response = try await responseTask.value + let response = try await repository.getWeather() let weatherData = WeatherDataBuilder( response: response, options: buildWeatherDataOptions(for: unit), diff --git a/DatWeatherDoe/ViewModel/WeatherViewModelType.swift b/DatWeatherDoe/ViewModel/WeatherViewModelType.swift index 21003f6..bebb85a 100644 --- a/DatWeatherDoe/ViewModel/WeatherViewModelType.swift +++ b/DatWeatherDoe/ViewModel/WeatherViewModelType.swift @@ -8,7 +8,6 @@ import Combine -@MainActor protocol WeatherViewModelType: AnyObject { func setup(with formatter: WeatherDataFormatter) func getUpdatedWeatherAfterRefresh() From cd073649138bae8fab0e34de34371e7db881c70c Mon Sep 17 00:00:00 2001 From: Inder Dhir Date: Mon, 13 Oct 2025 22:24:41 -0400 Subject: [PATCH 2/5] Update calls to String localized --- DatWeatherDoe/API/Response/AirQuality.swift | 30 ++++--------------- DatWeatherDoe/API/WeatherError.swift | 4 +-- .../Configure/Options/RefreshInterval.swift | 8 ++--- .../Options/WeatherConditionPosition.swift | 4 +-- .../UI/Configure/Options/WeatherSource.swift | 9 ++---- .../WeatherConditionTextMapper.swift | 27 ++++++++--------- 6 files changed, 29 insertions(+), 53 deletions(-) diff --git a/DatWeatherDoe/API/Response/AirQuality.swift b/DatWeatherDoe/API/Response/AirQuality.swift index 4b6c201..dfc90e4 100644 --- a/DatWeatherDoe/API/Response/AirQuality.swift +++ b/DatWeatherDoe/API/Response/AirQuality.swift @@ -20,35 +20,17 @@ enum AirQualityIndex: Int, Decodable { var description: String { switch self { case .good: - NSLocalizedString( - "Good", - comment: "Air quality index: Good" - ) + String(localized: "Good") case .moderate: - NSLocalizedString( - "Moderate", - comment: "Air quality index: Moderate" - ) + String(localized: "Moderate") case .unhealthyForSensitive: - NSLocalizedString( - "Unhealthy for sensitive groups", - comment: "Air quality index: Unhealthy for sensitive groups" - ) + String(localized: "Unhealthy for sensitive groups") case .unhealthy: - NSLocalizedString( - "Unhealthy", - comment: "Air quality index: Unhealthy" - ) + String(localized: "Unhealthy") case .veryUnhealthy: - NSLocalizedString( - "Very unhealthy", - comment: "Air quality index: Very unhealthy" - ) + String(localized: "Very unhealthy") case .hazardous: - NSLocalizedString( - "Hazardous", - comment: "Air quality index: Hazardous" - ) + String(localized: "Hazardous") } } } diff --git a/DatWeatherDoe/API/WeatherError.swift b/DatWeatherDoe/API/WeatherError.swift index 2c87b28..5663698 100644 --- a/DatWeatherDoe/API/WeatherError.swift +++ b/DatWeatherDoe/API/WeatherError.swift @@ -19,9 +19,9 @@ enum WeatherError: LocalizedError { case .unableToConstructUrl: "Unable to construct URL" case .locationError: - NSLocalizedString("❗️Location", comment: "Location error when fetching weather") + String(localized: "❗️Location") case .latLongIncorrect: - NSLocalizedString("❗️Lat/Long", comment: "Lat/Long error when fetching weather") + String(localized: "❗️Lat/Long") case .networkError: "🖧" } diff --git a/DatWeatherDoe/UI/Configure/Options/RefreshInterval.swift b/DatWeatherDoe/UI/Configure/Options/RefreshInterval.swift index d4b63ed..7f82aaa 100644 --- a/DatWeatherDoe/UI/Configure/Options/RefreshInterval.swift +++ b/DatWeatherDoe/UI/Configure/Options/RefreshInterval.swift @@ -19,13 +19,13 @@ enum RefreshInterval: TimeInterval, CaseIterable, Identifiable { var title: String { switch self { case .fiveMinutes: - NSLocalizedString("5 min", comment: "5 min refresh interval") + String(localized: "5 min") case .fifteenMinutes: - NSLocalizedString("15 min", comment: "15 min refresh interval") + String(localized: "15 min") case .thirtyMinutes: - NSLocalizedString("30 min", comment: "30 min refresh interval") + String(localized: "30 min") case .sixtyMinutes: - NSLocalizedString("60 min", comment: "60 min refresh interval") + String(localized: "60 min") } } } diff --git a/DatWeatherDoe/UI/Configure/Options/WeatherConditionPosition.swift b/DatWeatherDoe/UI/Configure/Options/WeatherConditionPosition.swift index b2ca775..e22ef34 100644 --- a/DatWeatherDoe/UI/Configure/Options/WeatherConditionPosition.swift +++ b/DatWeatherDoe/UI/Configure/Options/WeatherConditionPosition.swift @@ -16,9 +16,9 @@ enum WeatherConditionPosition: String, Identifiable { var title: String { switch self { case .beforeTemperature: - NSLocalizedString("Before Temperature", comment: "Weather condition before temperature") + String(localized: "Before Temperature") case .afterTemperature: - NSLocalizedString("After Temperature", comment: "Weather condition after temperature") + String(localized: "After Temperature") } } } diff --git a/DatWeatherDoe/UI/Configure/Options/WeatherSource.swift b/DatWeatherDoe/UI/Configure/Options/WeatherSource.swift index 90789e9..ed3312e 100644 --- a/DatWeatherDoe/UI/Configure/Options/WeatherSource.swift +++ b/DatWeatherDoe/UI/Configure/Options/WeatherSource.swift @@ -14,9 +14,9 @@ enum WeatherSource: String, CaseIterable { var title: String { switch self { case .location: - NSLocalizedString("Location", comment: "Weather based on location") + String(localized: "Location") case .latLong: - NSLocalizedString("Lat/Long", comment: "Weather based on Lat/Long") + String(localized: "Lat/Long") } } @@ -34,10 +34,7 @@ enum WeatherSource: String, CaseIterable { case .location: "" case .latLong: - NSLocalizedString( - "[latitude],[longitude]", - comment: "Placeholder hint for entering Lat/Long" - ) + String(localized: "[latitude],[longitude]") } } } diff --git a/DatWeatherDoe/UI/Decorator/Condition/WeatherConditionTextMapper.swift b/DatWeatherDoe/UI/Decorator/Condition/WeatherConditionTextMapper.swift index b04ca26..797e4f3 100644 --- a/DatWeatherDoe/UI/Decorator/Condition/WeatherConditionTextMapper.swift +++ b/DatWeatherDoe/UI/Decorator/Condition/WeatherConditionTextMapper.swift @@ -16,43 +16,40 @@ final class WeatherConditionTextMapper: WeatherConditionTextMapperType { func map(_ condition: WeatherCondition) -> String { switch condition { case .cloudy: - NSLocalizedString("Cloudy", comment: "Cloudy weather condition") + String(localized: "Cloudy") case .partlyCloudy, .partlyCloudyNight: - NSLocalizedString("Partly cloudy", comment: "Partly cloudy weather condition") + String(localized: "Partly cloudy") case .sunny: - NSLocalizedString("Sunny", comment: "Sunny weather condition") + String(localized: "Sunny") case .clearNight: - NSLocalizedString("Clear", comment: "Clear at night weather condition") + String(localized: "Clear") case .snow: - NSLocalizedString("Snow", comment: "Snow weather condition") + String(localized: "Snow") case .heavyRain: - NSLocalizedString("Heavy rain", comment: "Heavy rain weather condition") + String(localized: "Heavy rain") case .freezingRain: - NSLocalizedString("Freezing rain", comment: "Freezing rain weather condition") + String(localized: "Freezing rain") case .lightRain: - NSLocalizedString("Light rain", comment: "Light rain weather condition") + String(localized: "Light rain") case .partlyCloudyRain: - NSLocalizedString( - "Partly cloudy with rain", - comment: "Partly cloudy with rain weather condition" - ) + String(localized: "Partly cloudy with rain") case .thunderstorm: - NSLocalizedString("Thunderstorm", comment: "Thunderstorm weather condition") + String(localized: "Thunderstorm") case .mist: - NSLocalizedString("Mist", comment: "Mist weather condition") + String(localized: "Mist") case .fog: - NSLocalizedString("Fog", comment: "Fog weather condition") + String(localized: "Fog") } } } From 4b240c2d5b3e4895d3734f7e6b21c25a8fe2755a Mon Sep 17 00:00:00 2001 From: Inder Dhir Date: Mon, 13 Oct 2025 22:50:01 -0400 Subject: [PATCH 3/5] Add retries for location fetching --- DatWeatherDoe.xcodeproj/project.pbxproj | 4 ++ .../System/SystemLocationFetcher.swift | 22 ++++++----- .../Repository/System/Task+Retry.swift | 38 +++++++++++++++++++ 3 files changed, 55 insertions(+), 9 deletions(-) create mode 100644 DatWeatherDoe/ViewModel/Repository/System/Task+Retry.swift diff --git a/DatWeatherDoe.xcodeproj/project.pbxproj b/DatWeatherDoe.xcodeproj/project.pbxproj index 3130153..08e5ce3 100644 --- a/DatWeatherDoe.xcodeproj/project.pbxproj +++ b/DatWeatherDoe.xcodeproj/project.pbxproj @@ -63,6 +63,7 @@ 20BBCDAA278B8A18007DEEB0 /* WeatherViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20BBCDA9278B8A18007DEEB0 /* WeatherViewModel.swift */; }; 20BBCDAD278B8B28007DEEB0 /* WeatherViewModelType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20BBCDAC278B8B28007DEEB0 /* WeatherViewModelType.swift */; }; 20BBCDAF278B92A7007DEEB0 /* SystemLocationFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20BBCDAE278B92A7007DEEB0 /* SystemLocationFetcher.swift */; }; + 20C6722A2E9DEDCB00A577C1 /* Task+Retry.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20C672292E9DEDCB00A577C1 /* Task+Retry.swift */; }; 20CA6E11278F49AC00FFC53A /* TemperatureFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20CA6E10278F49AC00FFC53A /* TemperatureFormatter.swift */; }; 20D857102A8317F5005727BB /* ConfigureUnitOptionsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20D8570F2A8317F5005727BB /* ConfigureUnitOptionsView.swift */; }; 20D857122A831802005727BB /* ConfigureWeatherOptionsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20D857112A831802005727BB /* ConfigureWeatherOptionsView.swift */; }; @@ -146,6 +147,7 @@ 20BBCDA9278B8A18007DEEB0 /* WeatherViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeatherViewModel.swift; sourceTree = ""; }; 20BBCDAC278B8B28007DEEB0 /* WeatherViewModelType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeatherViewModelType.swift; sourceTree = ""; }; 20BBCDAE278B92A7007DEEB0 /* SystemLocationFetcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SystemLocationFetcher.swift; sourceTree = ""; }; + 20C672292E9DEDCB00A577C1 /* Task+Retry.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Task+Retry.swift"; sourceTree = ""; }; 20CA6E10278F49AC00FFC53A /* TemperatureFormatter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TemperatureFormatter.swift; sourceTree = ""; }; 20CB480B27B83EF90043C60F /* Config.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Config.xcconfig; sourceTree = ""; }; 20D8570F2A8317F5005727BB /* ConfigureUnitOptionsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigureUnitOptionsView.swift; sourceTree = ""; }; @@ -429,6 +431,7 @@ children = ( 20B468212793B85900FC6050 /* SystemLocationWeatherRepository.swift */, 20BBCDAE278B92A7007DEEB0 /* SystemLocationFetcher.swift */, + 20C672292E9DEDCB00A577C1 /* Task+Retry.swift */, ); path = System; sourceTree = ""; @@ -678,6 +681,7 @@ 20BBCDAD278B8B28007DEEB0 /* WeatherViewModelType.swift in Sources */, 20D8571F2A832AC6005727BB /* TemperatureUnit.swift in Sources */, 20D857102A8317F5005727BB /* ConfigureUnitOptionsView.swift in Sources */, + 20C6722A2E9DEDCB00A577C1 /* Task+Retry.swift in Sources */, 202B1029278D5A7100ED6D42 /* WeatherForecaster.swift in Sources */, 206523C826597B120026C506 /* WeatherError.swift in Sources */, 209F8A38279136D300EB5C45 /* LocationValidator.swift in Sources */, diff --git a/DatWeatherDoe/ViewModel/Repository/System/SystemLocationFetcher.swift b/DatWeatherDoe/ViewModel/Repository/System/SystemLocationFetcher.swift index 9c06838..a9bb664 100644 --- a/DatWeatherDoe/ViewModel/Repository/System/SystemLocationFetcher.swift +++ b/DatWeatherDoe/ViewModel/Repository/System/SystemLocationFetcher.swift @@ -83,15 +83,9 @@ final actor SystemLocationFetcher: NSObject, SystemLocationFetcherType { } do { - let latestLocation = try await withCheckedThrowingContinuation { continuation in - locationUpdateContinuation = continuation - - Task { @MainActor in - locationManager.startUpdatingLocation() - } - } - - locationUpdateContinuation = nil + let latestLocation = try await Task.retrying { + try await self.requestLocation() + }.value return latestLocation } catch { @@ -108,6 +102,16 @@ final actor SystemLocationFetcher: NSObject, SystemLocationFetcherType { return nil } + + private func requestLocation() async throws -> CLLocationCoordinate2D { + try await withCheckedThrowingContinuation { continuation in + locationUpdateContinuation = continuation + + Task { @MainActor in + locationManager.startUpdatingLocation() + } + } + } } // MARK: CLLocationManagerDelegate diff --git a/DatWeatherDoe/ViewModel/Repository/System/Task+Retry.swift b/DatWeatherDoe/ViewModel/Repository/System/Task+Retry.swift new file mode 100644 index 0000000..cb01591 --- /dev/null +++ b/DatWeatherDoe/ViewModel/Repository/System/Task+Retry.swift @@ -0,0 +1,38 @@ +// +// Task+Retry.swift +// DatWeatherDoe +// +// Created by Inder Dhir on 10/13/25. +// Copyright © 2025 Inder Dhir. All rights reserved. +// + +import Foundation + +extension Task where Failure == Error { + + @discardableResult + static func retrying( + priority: TaskPriority? = nil, + maxRetryCount: Int = 3, + retryDelay: TimeInterval = 1, + operation: @Sendable @escaping () async throws -> Success + ) -> Task { + Task(priority: priority) { + for _ in 0...sleep(nanoseconds: delay) + + continue + } + } + + try Task.checkCancellation() + + return try await operation() + } + } +} From 9bd4e1faa7e02dfd03a7a9ad8ff547342fe329d6 Mon Sep 17 00:00:00 2001 From: Inder Dhir Date: Mon, 13 Oct 2025 22:51:00 -0400 Subject: [PATCH 4/5] Update version to 5.6.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 08e5ce3..1b06505 100644 --- a/DatWeatherDoe.xcodeproj/project.pbxproj +++ b/DatWeatherDoe.xcodeproj/project.pbxproj @@ -861,7 +861,7 @@ CODE_SIGN_IDENTITY = "Mac Developer"; "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Mac Developer"; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 54; + CURRENT_PROJECT_VERSION = 55; DEAD_CODE_STRIPPING = YES; DEVELOPMENT_ASSET_PATHS = "\"DatWeatherDoe/Resources/DevelopmentAssets\" \"DatWeatherDoe/Resources/DevelopmentAssets/TestData.swift\""; DEVELOPMENT_TEAM = Q8X4D3A8MT; @@ -876,7 +876,7 @@ "$(inherited)", "@executable_path/../Frameworks", ); - MARKETING_VERSION = 5.5.1; + MARKETING_VERSION = 5.6.0; PRODUCT_BUNDLE_IDENTIFIER = com.inderdhir.DatWeatherDoe.debug; PRODUCT_NAME = DatWeatherDoe; PROVISIONING_PROFILE = ""; @@ -895,7 +895,7 @@ CODE_SIGN_IDENTITY = "Mac Developer"; "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Mac Developer"; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 54; + CURRENT_PROJECT_VERSION = 55; DEAD_CODE_STRIPPING = YES; DEVELOPMENT_ASSET_PATHS = "\"DatWeatherDoe/Resources/DevelopmentAssets\" \"DatWeatherDoe/Resources/DevelopmentAssets/TestData.swift\""; DEVELOPMENT_TEAM = Q8X4D3A8MT; @@ -910,7 +910,7 @@ "$(inherited)", "@executable_path/../Frameworks", ); - MARKETING_VERSION = 5.5.1; + MARKETING_VERSION = 5.6.0; PRODUCT_BUNDLE_IDENTIFIER = com.inderdhir.DatWeatherDoe; PRODUCT_NAME = DatWeatherDoe; PROVISIONING_PROFILE = ""; From b0a9aebaa002d32608d38aed41b0ccb2d893d21d Mon Sep 17 00:00:00 2001 From: Inder Dhir Date: Sat, 25 Oct 2025 14:36:46 -0400 Subject: [PATCH 5/5] Fix an issue with weather location text --- DatWeatherDoe/UI/Configure/ConfigureViewModel.swift | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/DatWeatherDoe/UI/Configure/ConfigureViewModel.swift b/DatWeatherDoe/UI/Configure/ConfigureViewModel.swift index aaea19a..5492550 100644 --- a/DatWeatherDoe/UI/Configure/ConfigureViewModel.swift +++ b/DatWeatherDoe/UI/Configure/ConfigureViewModel.swift @@ -14,13 +14,9 @@ final class ConfigureViewModel: ObservableObject { didSet { configManager.measurementUnit = measurementUnit.rawValue } } - @Published var weatherSource: WeatherSource { - didSet { configManager.weatherSource = weatherSource.rawValue } - } + @Published var weatherSource: WeatherSource - @Published var weatherSourceText: String { - didSet { configManager.weatherSourceText = weatherSourceText } - } + @Published var weatherSourceText: String @Published var refreshInterval: RefreshInterval { didSet { configManager.refreshInterval = refreshInterval.rawValue } @@ -68,6 +64,7 @@ final class ConfigureViewModel: ObservableObject { self.configManager = configManager measurementUnit = configManager.parsedMeasurementUnit + weatherSource = WeatherSource(rawValue: configManager.weatherSource) ?? .location weatherSourceText = configManager.weatherSourceText @@ -92,6 +89,8 @@ final class ConfigureViewModel: ObservableObject { func saveConfig() { configManager.updateWeatherSource(weatherSource, sourceText: weatherSourceText) + weatherSourceText = configManager.weatherSourceText + configManager.setConfigOptions( .init( refreshInterval: refreshInterval,