-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #43 from soucolline/develop
Version 2.2.0
- Loading branch information
Showing
93 changed files
with
1,169 additions
and
1,012 deletions.
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
name: run-tests | ||
on: | ||
pull_request: | ||
branches: | ||
- 'develop' | ||
jobs: | ||
build: | ||
runs-on: macos-12 | ||
steps: | ||
- uses: swift-actions/setup-swift@v1 | ||
- uses: maxim-lobanov/setup-xcode@v1 | ||
with: | ||
xcode-version: '14.0.1' | ||
- uses: actions/checkout@v3 | ||
- uses: maierj/[email protected] | ||
with: | ||
lane: 'tests' | ||
subdirectory: 'swiftUV/fastlane' |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
// swift-tools-version: 5.6 | ||
// The swift-tools-version declares the minimum version of Swift required to build this package. | ||
|
||
import PackageDescription | ||
|
||
let tca: Target.Dependency = .product(name: "ComposableArchitecture", package: "swift-composable-architecture") | ||
let tcaCoreLocation: Target.Dependency = .product(name: "ComposableCoreLocation", package: "composable-core-location") | ||
|
||
let package = Package( | ||
name: "uv-today-ios", | ||
platforms: [.iOS(.v15)], | ||
products: [ | ||
.library(name: "AppFeature", targets: ["AppFeature"]), | ||
.library(name: "LocationManager", targets: ["LocationManager"]), | ||
.library(name: "Models", targets: ["Models"]), | ||
.library(name: "UVClient", targets: ["UVClient"]) | ||
], | ||
dependencies: [ | ||
.package(url: "https://github.com/pointfreeco/swift-composable-architecture", exact: "0.43.0"), | ||
.package(url: "https://github.com/pointfreeco/composable-core-location", exact: "0.2.0"), | ||
], | ||
targets: [ | ||
.target( | ||
name: "AppFeature", | ||
dependencies: [ | ||
"LocationManager", | ||
"Models", | ||
"UVClient", | ||
tca, | ||
tcaCoreLocation | ||
] | ||
), | ||
.target( | ||
name: "LocationManager", | ||
dependencies: [ | ||
tcaCoreLocation | ||
] | ||
), | ||
.target(name: "Models"), | ||
.target( | ||
name: "UVClient", | ||
dependencies: [ | ||
"Models", | ||
tca | ||
] | ||
), | ||
.testTarget( | ||
name: "AppFeatureTests", | ||
dependencies: [ | ||
"AppFeature", | ||
"Models", | ||
"UVClient", | ||
tca | ||
] | ||
), | ||
.testTarget( | ||
name: "ModelsTests", | ||
dependencies: [ | ||
"Models" | ||
] | ||
) | ||
] | ||
) |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,3 @@ | ||
# SwiftUV | ||
Swift app to get current UV index depending on location | ||
# uv-today-ios | ||
|
||
A description of this package. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,194 @@ | ||
// | ||
// AppReducer.swift | ||
// swiftUV | ||
// | ||
// Created by Thomas Guilleminot on 03/08/2022. | ||
// Copyright © 2022 Thomas Guilleminot. All rights reserved. | ||
// | ||
|
||
import ComposableArchitecture | ||
import ComposableCoreLocation | ||
import LocationManager | ||
import Models | ||
import UVClient | ||
|
||
public struct AppReducer: ReducerProtocol { | ||
public struct State: Equatable { | ||
public var uvIndex: Index | ||
public var cityName: String | ||
public var weatherRequestInFlight: Bool | ||
public var getCityNameRequestInFlight: Bool | ||
public var attributionLogo: URL? | ||
public var attributionLink: URL? | ||
public var errorText: String | ||
public var userLocation: Models.Location? | ||
public var isRequestingCurrentLocation: Bool | ||
public var hasAlreadyRequestLocation: Bool | ||
public var isLocationRefused: Bool | ||
|
||
@BindableState public var shouldShowErrorPopup: Bool | ||
|
||
public init( | ||
uvIndex: Index = 0, | ||
cityName: String = "loading", | ||
weatherRequestInFlight: Bool = false, | ||
getCityNameRequestInFlight: Bool = false, | ||
errorText: String = "", | ||
userLocation: Models.Location? = nil, | ||
isRequestingCurrentLocation: Bool = false, | ||
hasAlreadyRequestLocation: Bool = false, | ||
isLocationRefused: Bool = false, | ||
shouldShowErrorPopup: Bool = false | ||
) { | ||
self.uvIndex = uvIndex | ||
self.cityName = cityName | ||
self.weatherRequestInFlight = weatherRequestInFlight | ||
self.getCityNameRequestInFlight = getCityNameRequestInFlight | ||
self.errorText = errorText | ||
self.userLocation = userLocation | ||
self.isRequestingCurrentLocation = isRequestingCurrentLocation | ||
self.hasAlreadyRequestLocation = hasAlreadyRequestLocation | ||
self.isLocationRefused = isLocationRefused | ||
self.shouldShowErrorPopup = shouldShowErrorPopup | ||
} | ||
} | ||
|
||
public enum Action: Equatable, BindableAction { | ||
case getUVRequest | ||
case getUVResponse(TaskResult<Index>) | ||
case getCityNameResponse(TaskResult<String>) | ||
case getAttribution | ||
case getAttributionResponse(TaskResult<AttributionResponse>) | ||
|
||
case onAppear | ||
case onDisappear | ||
case locationManager(LocationManager.Action) | ||
case binding(BindingAction<State>) | ||
} | ||
|
||
@Dependency(\.uvClient) public var uvClient: UVClient | ||
@Dependency(\.locationManager) public var locationManager: LocationManager | ||
|
||
public init() {} | ||
|
||
public var body: some ReducerProtocol<State, Action> { | ||
BindingReducer() | ||
|
||
Reduce { state, action in | ||
switch action { | ||
case .onAppear: | ||
state.weatherRequestInFlight = true | ||
state.getCityNameRequestInFlight = true | ||
state.isRequestingCurrentLocation = true | ||
state.isLocationRefused = false | ||
|
||
switch locationManager.authorizationStatus() { | ||
case .notDetermined: | ||
return .merge( | ||
locationManager | ||
.delegate() | ||
.map(AppReducer.Action.locationManager), | ||
|
||
locationManager | ||
.requestWhenInUseAuthorization() | ||
.fireAndForget() | ||
) | ||
|
||
case .authorizedAlways, .authorizedWhenInUse: | ||
return .merge( | ||
locationManager | ||
.delegate() | ||
.map(AppReducer.Action.locationManager), | ||
|
||
locationManager | ||
.requestLocation() | ||
.fireAndForget() | ||
) | ||
|
||
case .restricted, .denied: | ||
state.shouldShowErrorPopup = true | ||
state.errorText = "app.error.localisationDisabled".localized | ||
state.isLocationRefused = true | ||
return .none | ||
|
||
@unknown default: | ||
return .none | ||
} | ||
|
||
case .onDisappear: | ||
state.hasAlreadyRequestLocation = false | ||
return .none | ||
|
||
case .getUVRequest: | ||
state.weatherRequestInFlight = true | ||
state.getCityNameRequestInFlight = true | ||
|
||
guard let location = state.userLocation else { | ||
state.shouldShowErrorPopup = true | ||
state.errorText = "app.error.couldNotLocalise".localized | ||
return .none | ||
} | ||
|
||
return .run { send in | ||
async let fetchUV: Void = send( | ||
.getUVResponse(TaskResult { try await uvClient.fetchUVIndex(UVClientRequest(lat: location.latitude, long: location.longitude)) }) | ||
) | ||
|
||
async let fetchCityName: Void = send( | ||
.getCityNameResponse(TaskResult { try await uvClient.fetchCityName(location) }) | ||
) | ||
|
||
_ = await [fetchUV, fetchCityName] | ||
} | ||
|
||
case .getUVResponse(.success(let index)): | ||
state.weatherRequestInFlight = false | ||
state.uvIndex = index | ||
return .none | ||
|
||
case .getUVResponse(.failure(let error)): | ||
state.weatherRequestInFlight = false | ||
state.shouldShowErrorPopup = true | ||
state.errorText = error.localizedDescription | ||
state.uvIndex = 0 | ||
return .none | ||
|
||
case .getCityNameResponse(.success(let city)): | ||
state.getCityNameRequestInFlight = false | ||
state.cityName = city | ||
return .none | ||
|
||
case .getCityNameResponse(.failure): | ||
state.getCityNameRequestInFlight = false | ||
state.cityName = "app.label.unknown".localized | ||
return .none | ||
|
||
case .getAttribution: | ||
return .task { | ||
await .getAttributionResponse(TaskResult { try await uvClient.fetchWeatherKitAttribution() }) | ||
} | ||
|
||
case .getAttributionResponse(.success(let attribution)): | ||
state.attributionLogo = attribution.logo | ||
state.attributionLink = attribution.link | ||
return .none | ||
|
||
case .getAttributionResponse(.failure): | ||
return .none | ||
|
||
case .binding(\.$shouldShowErrorPopup): | ||
state.shouldShowErrorPopup = false | ||
return .none | ||
|
||
case .binding: | ||
return .none | ||
|
||
case .locationManager: | ||
return .none | ||
} | ||
} | ||
._printChanges() | ||
|
||
LocationReducer() | ||
} | ||
} |
Oops, something went wrong.