Last active
August 31, 2020 11:08
-
-
Save a-voronov/aab3ba20d1807e71b7b2ecacec13be01 to your computer and use it in GitHub Desktop.
Loading Status FSM 🚥
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
/// Represents loading status, consisting of multiple steps. | |
/// All values' types can be specified depending on the problem. | |
enum AnyLoadingStatus<Loading, Loaded, Failed, Error: Swift.Error> { | |
case idle | |
case loading(Loading) | |
case loaded(Loaded) | |
case failed(Failed, Error) | |
} | |
extension AnyLoadingStatus { | |
var isIdle: Bool { | |
guard case .idle = self else { return false } | |
return true | |
} | |
var isLoading: Bool { | |
guard case .loading = self else { return false } | |
return true | |
} | |
var isLoaded: Bool { | |
guard case .loaded = self else { return false } | |
return true | |
} | |
var isFailed: Bool { | |
guard case .failed = self else { return false } | |
return true | |
} | |
var isStarted: Bool { | |
return !isIdle | |
} | |
var isFinished: Bool { | |
return isLoaded || isFailed | |
} | |
} | |
extension AnyLoadingStatus: Equatable where Loading: Equatable, Loaded: Equatable, Failed: Equatable, Error: Equatable {} | |
extension AnyLoadingStatus: Hashable where Loading: Hashable, Loaded: Hashable, Failed: Hashable, Error: Hashable {} | |
extension AnyLoadingStatus where Loading == Void { | |
static var loading: AnyLoadingStatus { | |
return .loading(()) | |
} | |
} | |
extension AnyLoadingStatus where Loaded == Void { | |
static var loaded: AnyLoadingStatus { | |
return .loaded(()) | |
} | |
} | |
extension AnyLoadingStatus where Failed == Void { | |
static func failed(_ error: Error) -> AnyLoadingStatus { | |
return .failed((), error) | |
} | |
} |
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
/// Represents just loading status without any data associated with it. | |
typealias JustLoadingStatus<Error: Swift.Error> = AnyLoadingStatus<Void, Void, Void, Error> |
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
/// Most commonly used variation with the same value type. | |
/// Can be treated as a FSM. | |
/// | |
/// - Loading: Optional type that represents two cases: | |
/// * nil means we're in a loading state without any successfully preceding case: | |
/// | |
/// `idle -> loading(nil)` | |
/// | |
/// `idle -> loading(nil) -> failed(nil, err) -> loading(nil)` | |
/// | |
/// * value means we're in a loading state with previous successfully loaded value: | |
/// | |
/// `idle -> loading(nil) -> loaded(value) -> loading(value)` | |
/// | |
/// - Loaded: Always contains currently loaded value. | |
/// | |
/// * `idle -> loading(nil) -> loaded(value) -> loading(value) -> loaded(newValue)` | |
/// | |
/// - Failed: Optional type that represents two cases: | |
/// | |
/// * nil means we're in a failed state without any successfully preceding case: | |
/// | |
/// `idle -> loading(nil) -> failed(nil, err)` | |
/// | |
/// `idle -> loading(nil) -> failed(nil, err) -> loading(nil) -> failed(nil, newErr)` | |
/// | |
/// * value means we're in a failed state with previous successfully loaded value: | |
/// | |
/// `idle -> loading(nil) -> loaded(value) -> loading(value) -> failed(value, err)` | |
/// | |
/// `idle -> loading(nil) -> loaded(value) -> loading(value) -> failed(value, err) -> loading(value) -> failed(value, newErr)` | |
/// | |
typealias LoadingStatus<Value, Error: Swift.Error> = AnyLoadingStatus<Value?, Value, Value?, Error> | |
extension AnyLoadingStatus where Loading == Loaded?, Failed == Loaded? { | |
var value: Loaded? { | |
get { | |
switch self { | |
case .idle: return nil | |
case let .loaded(value): return value | |
case let .loading(value), let .failed(value, _): return value | |
} | |
} | |
set { | |
guard let newValue = newValue else { return } | |
switch self { | |
case .idle: return | |
case .loaded: self = .loaded(newValue) | |
case .loading: self = .loading(newValue) | |
case .failed(_, let error): self = .failed(newValue, error) | |
} | |
} | |
} | |
var loadingValue: Loaded? { | |
guard case let .loading(value) = self else { return nil } | |
return value | |
} | |
var loadedValue: Loaded? { | |
guard case let .loaded(value) = self else { return nil } | |
return value | |
} | |
var failedValue: Loaded? { | |
guard case let .failed(value, _) = self else { return nil } | |
return value | |
} | |
var error: Error? { | |
guard case let .failed(_, error) = self else { return nil } | |
return error | |
} | |
var finishedResult: Result<Loaded, Error>? { | |
switch self { | |
case .idle, .loading: return nil | |
case let .loaded(value): return .success(value) | |
case let .failed(_, error): return .failure(error) | |
} | |
} | |
func map<U>(_ transform: (Loaded) -> U) -> LoadingStatus<U, Error> { | |
switch self { | |
case .idle: return .idle | |
case let .loading(value): return .loading(value.map(transform)) | |
case let .loaded(value): return .loaded(transform(value)) | |
case let .failed(value, error): return .failed(value.map(transform), error) | |
} | |
} | |
mutating func startLoading(keepingValue: Bool = true) { | |
guard !isLoading else { return } | |
self = .loading(keepingValue ? value : nil) | |
} | |
mutating func finish(with result: Result<Loaded, Error>, keepingValueIfFailed: Bool = true, force: Bool = false) { | |
guard isLoading || force else { return } | |
switch result { | |
case let .success(value): self = .loaded(value) | |
case let .failure(error): self = .failed(keepingValueIfFailed ? value : nil, error) | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Example