Skip to content

Instantly share code, notes, and snippets.

@erenkabakci
Created April 6, 2023 10:31
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 erenkabakci/03a252cb1509e428b6808c4e216cea69 to your computer and use it in GitHub Desktop.
Save erenkabakci/03a252cb1509e428b6808c4e216cea69 to your computer and use it in GitHub Desktop.
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