Skip to content

Instantly share code, notes, and snippets.

@soxjke
Last active October 30, 2017 19:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save soxjke/3efeaef6d762a33df3ac459489d54fe1 to your computer and use it in GitHub Desktop.
Save soxjke/3efeaef6d762a33df3ac459489d54fe1 to your computer and use it in GitHub Desktop.
import Foundation
import Redux_ReactiveSwift
extension AppState: Defaultable {
static var defaultValue: AppState = AppState(location: AppLocation(locationState: .notYetRequested,
locationRequestState: .none),
weather: AppWeather(geopositionRequestState: .none,
weatherRequestState: .none))
}
final class AppStore: Store<AppState, AppEvent> {
static let shared: AppStore = AppStore()
private init() {
super.init(state: AppState.defaultValue, reducers: [self.reducer])
}
required init(state: AppState, reducers: [AppStore.Reducer]) {
fatalError("init(state:reducers:) cannot be called on type AppStore. Use `shared` accessor")
}
private func reducer(state: AppState, event: AppEvent) -> AppState {
return AppState(location: locationReducer(state.location, event),
weather: weatherReducer(state.weather, event))
}
// MARK: Location reducers
private func locationReducer(_ state: AppLocation, _ event: AppEvent) -> AppLocation {
switch event {
case .locationPermissionResult(let success): return location(permissionResult: success, previous: state)
case .locationRequest: return locationRequest(previous: state)
case .locationResult(let latitude, let longitude, let timestamp, let error):
return locationResult(latitude: latitude, longitude: longitude, timestamp: timestamp, error: error, previous: state)
default: return state
}
}
private func location(permissionResult: Bool, previous: AppLocation) -> AppLocation {
return AppLocation(locationState: permissionResult ? .available : .notAvailable,
locationRequestState: previous.locationRequestState)
}
private func locationRequest(previous: AppLocation) -> AppLocation {
guard case .available = previous.locationState else { return previous }
if case .success(_, _, let timestamp) = previous.locationRequestState {
if (Date().timeIntervalSince1970 - timestamp < 300) { // Don't do update if location succeeded within last 5 minutes
return previous
}
}
return AppLocation(locationState: previous.locationState, locationRequestState: .updating) // Perform location update
}
private func locationResult(latitude: Double, longitude: Double, timestamp: TimeInterval, error: Swift.Error?, previous: AppLocation) -> AppLocation {
guard let error = error else {
return AppLocation(locationState: previous.locationState, locationRequestState: .success(latitude: latitude, longitude: longitude, timestamp: timestamp))
}
return AppLocation(locationState: previous.locationState, locationRequestState: .error(error: error))
}
// MARK: Weather reducers
private func weatherReducer(_ state: AppWeather, _ event: AppEvent) -> AppWeather {
switch event {
case .geopositionRequest: return geopositionRequest(previous: state)
case .geopositionResult(let geoposition, let error): return geopositionResult(geoposition: geoposition, error: error, previous: state)
case .weatherRequest: return weatherRequest(previous: state)
case .weatherResult(let current, let forecast, let error): return weatherResult(current: current, forecast: forecast, error: error, previous: state)
default: return state
}
}
private func geopositionRequest(previous: AppWeather) -> AppWeather {
return AppWeather(geopositionRequestState: .updating, weatherRequestState: previous.weatherRequestState)
}
private func geopositionResult(geoposition: Geoposition, error: Swift.Error?, previous: AppWeather) -> AppWeather {
guard let error = error else {
return AppWeather(geopositionRequestState: .success(geoposition: geoposition), weatherRequestState: previous.weatherRequestState)
}
return AppWeather(geopositionRequestState: .error(error: error), weatherRequestState: previous.weatherRequestState)
}
private func weatherRequest(previous: AppWeather) -> AppWeather {
return AppWeather(geopositionRequestState: previous.geopositionRequestState, weatherRequestState: .updating)
}
private func weatherResult(current: Weather, forecast: [Weather], error: Swift.Error?, previous: AppWeather) -> AppWeather {
guard let error = error else {
return AppWeather(geopositionRequestState: previous.geopositionRequestState,
weatherRequestState: .success(currentWeather: current, forecast: forecast))
}
return AppWeather(geopositionRequestState: previous.geopositionRequestState, weatherRequestState: .error(error: error))
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment