Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 12 additions & 4 deletions DatWeatherDoe.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -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 */; };
Expand Down Expand Up @@ -146,6 +147,7 @@
20BBCDA9278B8A18007DEEB0 /* WeatherViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeatherViewModel.swift; sourceTree = "<group>"; };
20BBCDAC278B8B28007DEEB0 /* WeatherViewModelType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeatherViewModelType.swift; sourceTree = "<group>"; };
20BBCDAE278B92A7007DEEB0 /* SystemLocationFetcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SystemLocationFetcher.swift; sourceTree = "<group>"; };
20C672292E9DEDCB00A577C1 /* Task+Retry.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Task+Retry.swift"; sourceTree = "<group>"; };
20CA6E10278F49AC00FFC53A /* TemperatureFormatter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TemperatureFormatter.swift; sourceTree = "<group>"; };
20CB480B27B83EF90043C60F /* Config.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Config.xcconfig; sourceTree = "<group>"; };
20D8570F2A8317F5005727BB /* ConfigureUnitOptionsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigureUnitOptionsView.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -429,6 +431,7 @@
children = (
20B468212793B85900FC6050 /* SystemLocationWeatherRepository.swift */,
20BBCDAE278B92A7007DEEB0 /* SystemLocationFetcher.swift */,
20C672292E9DEDCB00A577C1 /* Task+Retry.swift */,
);
path = System;
sourceTree = "<group>";
Expand Down Expand Up @@ -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 */,
Expand Down Expand Up @@ -857,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;
Expand All @@ -872,10 +876,12 @@
"$(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 = "";
SWIFT_APPROACHABLE_CONCURRENCY = YES;
SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor;
SWIFT_STRICT_CONCURRENCY = complete;
SWIFT_SWIFT3_OBJC_INFERENCE = Default;
};
Expand All @@ -889,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;
Expand All @@ -904,10 +910,12 @@
"$(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 = "";
SWIFT_APPROACHABLE_CONCURRENCY = YES;
SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor;
SWIFT_STRICT_CONCURRENCY = complete;
SWIFT_SWIFT3_OBJC_INFERENCE = Default;
};
Expand Down
2 changes: 1 addition & 1 deletion DatWeatherDoe/API/NetworkClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
30 changes: 6 additions & 24 deletions DatWeatherDoe/API/Response/AirQuality.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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")
}
}
}
4 changes: 2 additions & 2 deletions DatWeatherDoe/API/WeatherError.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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:
"🖧"
}
Expand Down
4 changes: 1 addition & 3 deletions DatWeatherDoe/DatWeatherDoeApp.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
11 changes: 5 additions & 6 deletions DatWeatherDoe/UI/Configure/ConfigureViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
Expand Down Expand Up @@ -68,6 +64,7 @@ final class ConfigureViewModel: ObservableObject {
self.configManager = configManager

measurementUnit = configManager.parsedMeasurementUnit

weatherSource = WeatherSource(rawValue: configManager.weatherSource) ?? .location
weatherSourceText = configManager.weatherSourceText

Expand All @@ -92,6 +89,8 @@ final class ConfigureViewModel: ObservableObject {

func saveConfig() {
configManager.updateWeatherSource(weatherSource, sourceText: weatherSourceText)
weatherSourceText = configManager.weatherSourceText

configManager.setConfigOptions(
.init(
refreshInterval: refreshInterval,
Expand Down
8 changes: 4 additions & 4 deletions DatWeatherDoe/UI/Configure/Options/RefreshInterval.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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")
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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")
}
}
}
9 changes: 3 additions & 6 deletions DatWeatherDoe/UI/Configure/Options/WeatherSource.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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")
}
}

Expand All @@ -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]")
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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")
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,12 @@
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()
}
}

Expand Down Expand Up @@ -85,17 +83,9 @@
}

do {
let latestLocation = try await withCheckedThrowingContinuation { continuation in
Task(priority: .high) {
locationUpdateContinuation = continuation

await MainActor.run {
locationManager.startUpdatingLocation()
}
}
}

locationUpdateContinuation = nil
let latestLocation = try await Task.retrying {
try await self.requestLocation()
}.value

return latestLocation
} catch {
Expand All @@ -112,6 +102,16 @@

return nil
}

private func requestLocation() async throws -> CLLocationCoordinate2D {
try await withCheckedThrowingContinuation { continuation in
locationUpdateContinuation = continuation

Task { @MainActor in
locationManager.startUpdatingLocation()
}
}
}
}

// MARK: CLLocationManagerDelegate
Expand All @@ -120,22 +120,23 @@
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)
}
}
}

Check warning on line 142 in DatWeatherDoe/ViewModel/Repository/System/SystemLocationFetcher.swift

View workflow job for this annotation

GitHub Actions / lint

Trailing Newline Violation: Files should have a single trailing newline (trailing_newline)
Loading