ViewControllerWithLocalState
open class ViewControllerWithLocalState<V: ViewControllerModellableView & UIView>: ViewController<V>
where V.VM: ViewModelWithLocalState
Special case of a ViewController
that contains a LocalState
.
Overview
A ViewController
is managing the UI of a screen, listening for Katana global state changes, keeping the UI updated
and dispatching actions in response to user interactions in order to change the global state.
There are times when you have some kind of state information that is only specific to the screen managed
by a ViewController, like for instance the item selected in a list.
In this case, in order to avoid polluting the global state, you can represent that information inside
a LocalState
and promote that ViewController to be a ViewControllerWithLocalState
.
A ViewControllerWithLocalState contains a localState
variable that you can change directly in order
to represent local state changes.
The ViewControllerWithLocalState will be listening for changes of both global and local states, updating
the UI using the appropriate ViewModelWithLocalState
.
You can change the global state dispatching Katana actions like in every ViewController
. You can change
the LocalState
manipulating directly the localState
variable.
The lifecycle of a ViewControllerWithLocalState is the same as a normal ViewController
, please refer to that
for more details.
struct GlobalState: State {
var todos: [String] = [
"buy milk",
"find a unicorn",
"visit Rome"
]
}
struct RemoveTodo: AppAction {
var index: Int
func updatedState(inout currentState: GlobalState) {
currentState.todos.remove(at: index)
}
}
struct ListLocalState: LocalState {
var selectedIndex: Int
}
struct TodolistViewModel: ViewModelWithLocalState {
var todos: [String]
var selectedIndex: Int
init?(state: GlobalState?, localState: ListLocalState) {
guard let state = state else { return nil }
self.todos = state.todos
self.selectedIndex = localState.selectedIndex
}
}
class TodoListView: UIView, ViewControllerModellableView {
// subviews
var todoListView = ListView()
// interactions
var didTapToRemoveItem: ((Int) -> ())?
var didSelectItem: ((Int) -> ())?
// setup
func setup() {
self.todoListView.on(.selection) { [unowned self] indexPath in
self.didSelectItem?(indexPath.item)
}
self.todoListView.on(.deleteItem) { [unowned self] indexPath in
seld.didTapRemoveItem?(indexPath.item)
}
self.addSubview(self.todoListView)
}
// style
func style() {
self.backgroundColor = .white
self.todoListView.backgroundColor = .white
}
// update
func update(oldModel: CounterViewModel?) {
self.todoListView.source = model?.todos ?? []
self.todoListView.selectedIndex = model?.todos
}
// layout
override func layoutSubviews() {
self.todoListView.frame = self.bounds
}
}
class TodoListViewController: ViewControllerWithLocalState<TodoListView> {
override func setupInteraction() {
self.rootView.didTapRemoveItem = { [unowned self] index in
self.dispatch(RemoveTodo(index: index))
}
self.rootView.didSelectItem = { [unowned self] index in
self.localState.selectedIndex = index
}
}
-
The
LocalState
of this ViewController.Declaration
Swift
public var localState: V.VM.LS { get set }
-
This is the last value for global state we observed, we are saving this to be able to update the
ViewModelWithLocalState
when the local state changes and we are disconnected from the global state.Declaration
Swift
public var lastKnownState: V.VM.S?
-
Undocumented
Declaration
Swift
public init(store: PartialStore<V.VM.S>, localState: V.VM.LS, connected: Bool = false)
-
Required init.
Declaration
Swift
public required init?(coder _: NSCoder)
-
Called after the local state is updated, override point for subclasses.
Declaration
Swift
open func didUpdateLocalState()
-
Called just before the unsubscribe, override point for subclasses.
Declaration
Swift
override open func willUnsubscribe()