Created
April 6, 2023 10:31
-
-
Save erenkabakci/03a252cb1509e428b6808c4e216cea69 to your computer and use it in GitHub Desktop.
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
final class DepotViewModel: BaseViewModel<DepotViewModel.State, DepotViewModel.Action> { | |
struct State: Equatable, Copyable { | |
var showSearch = false | |
} | |
enum Action { | |
case .openSearch(let show): | |
showSearch(show: show) | |
} | |
} | |
func showSearch(show: Bool) { | |
updateState(changing: \.showSearch, to: show) | |
} | |
// updateState and perform methods are still public unfortunately. A developer can access them directly without another proxy method, which could increase the complexity for readability. | |
} | |
struct DepotOverviewView: View { | |
@ObservedObject var viewModel: DepotViewModel | |
var body: View { | |
SomeView | |
.sheet(isPresented: createShowSearchBinding(viewModel.state)) { | |
SBNavigationView(path: .constant(Routing.financeRouter.path)) { | |
SearchView(viewModel: viewModel.searchViewModel){ | |
viewModel.showSearch(show: false) | |
} | |
} | |
} | |
} | |
// This is the biggest downside of using the <State, Action> type. | |
// @Bindings are essential part of a SwiftUI based implementation and creating them with a custom getter-setter compared to using just '$viewmodel.published' notation is error prone and cumbersome. | |
// We need to maintain custom getter and setter implementations each time. | |
private func createShowSearchBinding(_ state: DepotViewModel.State) -> Binding<Bool> { | |
Binding( | |
get: { state.showSearch }, | |
set: { value in viewModel.perform(.openSearch(value)) } | |
) | |
} | |
} | |
public protocol ViewModeling: ObservableObject { | |
associatedtype State: Equatable | |
associatedtype Action | |
var uiState: State { get } | |
func perform(_ action: Action) | |
} | |
open class BaseViewModel<State: Equatable, Action>: ViewModeling { | |
public typealias State = State | |
public typealias Action = Action | |
public var uiState: State { internalState } | |
public var state: State { | |
didSet { internalState = state } | |
} | |
@Published var internalState: State | |
public init(state: State) { | |
self.internalState = state | |
self.state = state | |
} | |
open func perform(_ action: Action) { | |
assertionFailure("This function must be overriden by the subclass") | |
} | |
} | |
extension BaseViewModel where State: Copyable { | |
public func updateState<T>(changing path: WritableKeyPath<State, T>, to value: T) { | |
state = state.copy(changing: path, to: value) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment