VSM is a reactive architecture that is unidirectional, highly type-safe, behavior-driven, and clean. This repository hosts an open-source swift package framework for easily building features in VSM on app publicly available Apple platforms.
VSM stands for both "View State Model" and "Viewable State Machine". The first definition describes how a feature in VSM is structured, the second definition illustrates how information flows.
In VSM, the View renders the State. Each state may provide a Model. Each model contains the data and actions available in a given state. Each action in a model returns one or more new states. Any state changes will update the view.
- The VSM Documentation contains a complete framework reference, guides, and other learning resources
- Open the Demo App to see many different working examples of how to build features using the VSM pattern
The following are code excerpts of a feature that shows a blog entry from a data repository.
The state is usually defined as an enum or a struct and represents the states that the view can have. It also declares the data and actions available to the view for each model. Actions return one or more new states.
enum BlogEntryViewState {
case initialized(loaderModel: LoaderModeling)
case loading(errorModel: ErrorModeling?)
case loaded(blogModel: BlogModeling)
}
protocol LoaderModeling {
func load() -> AnyPublisher<BlogArticleViewState, Never>
}
protocol ErrorModeling {
var message: String { get }
func retry() -> AnyPublisher<BlogArticleViewState, Never>
}
protocol BlogModeling {
var title: String { get }
var text: String { get }
func refresh() -> AnyPublisher<BlogArticleViewState, Never>
}
The discrete models provide the data for a given view state and implement the business logic within the actions.
struct LoaderModel: LoaderModeling {
func load() -> AnyPublisher<BlogArticleViewState, Never> {
...
}
}
struct ErrorModel: ErrorModeling {
var message: String
func retry() -> AnyPublisher<BlogArticleViewState, Never> {
...
}
}
struct BlogModel: BlogModeling {
var title: String
var body: String
func refresh() -> AnyPublisher<BlogArticleViewState, Never> {
...
}
}
The view observes and renders the state using the ViewState
property wrapper. State changes will automatically update the view.
struct BlogEntryView: View {
@ViewState var state: BlogEntryViewState = .initialized(LoaderModel())
var body: some View {
switch state {
case .initialized(loaderModel: let loaderModel):
...
.onAppear {
$state.observe(loaderModel.load())
}
case .loading(errorModel: let errorModel):
...
case .loaded(blogModel: let blogModel)
...
Button("Reload") {
$state.observe(blogModel.refresh())
}
}
}
}
This example uses SwiftUI, but the framework is also designed to work seamlessly with UIKit.
For more detailed tutorials and documentation, visit the VSM Documentation
VSM for Apple platforms is owned and maintained by Wayfair.
See CONTRIBUTING.md.
See SECURITY.md.
VSM for Apple platforms is released under the MIT license. See LICENSE for details.