Created
September 3, 2023 16:13
-
-
Save ikesyo/b86158d882e9a3ef29fa247b29209369 to your computer and use it in GitHub Desktop.
ScreenState.swift
This file contains hidden or 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
public enum ScreenState<Value, Error: Swift.Error> { | |
case loading | |
case failed(Error) | |
case empty | |
case loaded(Value) | |
public var isLoading: Bool { | |
if case .loading = self { return true } | |
return false | |
} | |
public var isEmpty: Bool { | |
if case .empty = self { return true } | |
return false | |
} | |
public var value: Value? { | |
if case .loaded(let value) = self { return value } | |
return nil | |
} | |
public var error: Error? { | |
if case .failed(let error) = self { return error } | |
return nil | |
} | |
/// 自身が `.loaded` であれば状態を変更しない。そうでなければ状態を `.loading` に変更する。 | |
public mutating func setLoadingIfNeeded() { | |
switch self { | |
case .failed, .empty: | |
self = .loading | |
case .loading, .loaded: | |
break | |
} | |
} | |
/// 自身が `.loaded` 以外の状態であれば状態を `.failed` に変更し、trueを返す。 | |
/// そうでなければ状態を変更せず、falseを返す。 | |
public mutating func handleError(_ error: Error) -> Bool { | |
switch self { | |
case .loaded: | |
return false | |
case .loading, .failed, .empty: | |
self = .failed(error) | |
return true | |
} | |
} | |
} | |
public struct ScreenStateProjector<Value, Failure: Error, Loading: View, Empty: View, Loaded: View, Failed: View>: View { | |
public let state: ScreenState<Value, Failure> | |
private let refreshAction: (() -> Void)? | |
private var loading: () -> Loading | |
private var empty: ((() -> Void)?) -> Empty | |
private var loaded: (Value) -> Loaded | |
private var failed: (Failure, (() -> Void)?) -> Failed | |
public init( | |
_ state: ScreenState<Value, Failure>, | |
refreshAction: (() -> Void)?, | |
@ViewBuilder onLoading: @escaping (() -> Loading) = { | |
LoadingView(isLoading: true) | |
}, | |
@ViewBuilder onFailed: @escaping (Failure, (() -> Void)?) -> Failed, | |
@ViewBuilder onEmpty: @escaping ((() -> Void)?) -> Empty = { | |
EmptyStateView(action: $0) | |
}, | |
@ViewBuilder onLoaded: @escaping (Value) -> Loaded | |
) { | |
self.state = state | |
self.refreshAction = refreshAction | |
self.loading = onLoading | |
self.empty = onEmpty | |
self.loaded = onLoaded | |
self.failed = onFailed | |
} | |
public init( | |
_ state: ScreenState<Value, Failure>, | |
refreshAction: (() -> Void)?, | |
@ViewBuilder onLoading: @escaping (() -> Loading) = { | |
LoadingView(isLoading: true) | |
}, | |
@ViewBuilder onEmpty: @escaping ((() -> Void)?) -> Empty = { | |
EmptyStateView(action: $0) | |
}, | |
@ViewBuilder onLoaded: @escaping (Value) -> Loaded | |
) where Failed == ErrorMessageView<Failure> { | |
self.state = state | |
self.refreshAction = refreshAction | |
self.loading = onLoading | |
self.empty = onEmpty | |
self.loaded = onLoaded | |
self.failed = { | |
ErrorMessageView($0, action: $1) | |
} | |
} | |
public var body: some View { | |
switch state { | |
case .loading: | |
loading() | |
case .failed(let error): | |
failed(error, refreshAction) | |
case .empty: | |
empty(refreshAction) | |
case .loaded(let value): | |
loaded(value) | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment