Verge.swift
_ An easier way to get unidirectional data flow _
_ Supports concurrent processing _
https://vergegroup.github.io/Verge/documentation/verge/
News
Still in development
Next Verge implementation has started.
which uses swift-concurrency
Requirements
- Swift 5.5 +
- @available(iOS 10, macOS 10.13, tvOS 10, watchOS 3)
- UIKit
- SwiftUI
Verge is not Flux, it's store-pattern and super powerful.
Verge is a performant store-pattern based state management library for iOS.
Please see the website: https://vergegroup.org
And the article about store-pattern
What store-pattern is
The word 'store-pattern' is used on Vue.js documentation that about how we manage the state between multiple components.
Projects which use Verge
- Pairs for JP
- Pairs for TW/KR
- Pairs Engage
๐ YOUR APP or PROJECT!
We're welcome to publish here your application which powered by Verge!
Please Submit from Pull requests
Mininal usage exmaple - In UIView - UIViewController
State-management is everywhere, you can put a store and start state-management.
final class CropViewController: UIViewController {
private struct State: Equatable {
var isSelectingAspectRatio = false
}
private let store: Store<State, Never> = .init(initialState: .init())
private var subscriptions = Set<VergeAnyCancellable>()
override public func viewDidLoad() {
store.sinkState { [weak self] state in
guard let self = self else { return }
state.ifChanged(\.isSelectingAspectRatio) { value in
//
}
}
.store(in: &subscriptions)
}
func showAspectRatioSelection() {
store.commit {
$0.isSelectingAspectRatio = true
}
}
func hideAspectRatioSelection() {
store.commit {
$0.isSelectingAspectRatio = false
}
}
}
Advanced usage exmaple - UIKit / SwiftUI
Creating a view-model (meaning Store)
final class MyViewModel: StoreComponentType {
/// ๐ก The state declaration can be aslo inner-type.
/// As possible adding Equatable for better performance.
struct State: Equatable {
struct NestedState: Equatable {
...
}
var name: String = ""
var count: Int = 0
var nested: NestedState = .init()
}
/// ๐ก This is basically a template statement. You might have something type of `Store`.
let store: DefaultStore = .init(initialState: .init())
// MARK: - โ
These are actions as well as writing methods.
func myAction() {
// ๐ฅ Mutating a state
commit {
$0.name = "Hello, Verge"
}
}
func incrementAsync() {
/**
๐ฅ Asynchronously mutating.
Verge just provides thread-safety.
We should manage operations in the Action.
*/
DispatchQueue.global().async {
commit {
// We must finish here synchronously - here is a critical session.
$0.count += 1
}
}
}
}
In SwiftUI
struct MyView: View {
let store: MyViewModel
var body: some View {
// โ
Uses `StateReader` to read the state this clarifies where components need the state.
StateReader(store) { state in
Text(state.name)
Button(action: {
self.store.myAction()
}) {
Text("Action")
}
}
}
}
StateReader
supports to derive a part of the state like below.
StateReader(store.derived(.map(\.nested))) { state in
...
}
๐คฒ Verge has not gathered enough experience in SwiftUI. Please let us know your ideas that improve Verge using in SwiftUI! (in Discussions or Issues)
In UIKit
Binding with a view (or view controller)
final class MyViewController: UIViewController {
let viewModel: MyViewModel
...
var cancellable: VergeAnyCancellable?
init(viewModel: MyViewModel) {
self.viewModel = viewModel
// โ
Start subscribing the state.
self.cancellable = viewModel.sinkState { [weak self] (state: Changes<MyViewModel.State>) in
self?.update(state: state)
}
}
/**
Actually we don't need to create such as this method, but here is for better clarity in this document.
*/
private func update(state: Changes<MyViewModel.State>) {
/**
๐ก `Changes` is a reference-type, but it's immutable.
And so can not subscribe.
Why is it a reference-type? Because it's for reducing copying cost.
It can detect difference with previous value with it contains a previous value.
Which is using `.ifChanged` or `.takeIfChanged`.
*/
/// ๐ฅค An example that setting the value if the target value has updated.
state.ifChanged(\.name) { (name) in
// โ
`name` has changed! Update the text.
nameLabel.text = name
}
/// ๐ฅค An example that composing as a tuple to behave like RxSwift's combine latest.
state.ifChanged({ ($0.name, $0.count) }, ==) { (name, count) in
/**
Receives every time the tuple differs from the previous one.
This means it changes when anyone in the tuple changed
*/
nameLabel.text = name
countLabel.text = age.description
}
...
}
}
Supports Integrating with RxSwift
Verge supports to integrate with RxSwift that enables to create a stream of State
and Activity
.
To activate it, install VergeRx
module.
What differences between Flux library are
'store-pattern' is the core-concept of Flux. Flux works with the multiple restricted rules top of the 'store-pattern'.
This means we can start using like Flux without using Action, Muation payload values.
// โ๏ธ no needs to use.
enum Action {
case increment
case decrement
}
This declarations might be quite big cost of implementation in order to start to use Flux.
Verge does not have this rules, so we can do like this when we update the state.
// ๐ค just like this
extension MyStore {
func increment() {
commit {
$0.count += 1
}
}
}
let store: MyStore
store.increment()
It can be easy start.
Of cource, we can create the layer to manage strict action and mutation payload on top of the Verge primitive layer.
Because 'store-pattern' is core-concept of Flux.
--
Motivation
Verge focuses use-cases in the real-world
Recently, we could say the unidirectional data flow is a popular architecture such as flux.
Does store-pattern(flux) architecture have a good performance?
It depends. The performance will be the worst depends on how it is used.
However, most of the cases, we don't know the app we're creating how it will grow and scales.
While the application is scaling up, the performance might decrease by getting complexity.
To keep performance, we need to tune it up with several approaches.
Considering the performance takes time from the beginning.
it will make us be annoying to use flux architecture.
Verge is designed for use from small and supports to scale.
Setting Verge up quickly, and tune-up when we need it.
Verge automatically tune-up and shows us what makes performance badly while development from Xcode's documentation.
For example, Verge provides these stuff to tune performance up.
- Derived (Similar to facebookexperimental/Recoil's Selector)
- ORM
Supports volatile events
We use an event as Activity
that won't be stored in the state.
This concept would help us to describe something that is not easy to describe as a state in the client application.
Installation
CocoaPods
Verge (core module)
pod 'Verge/Store'
VergeORM
pod 'Verge/ORM'
VergeRx
pod 'Verge/Rx'
These are separated with subspecs in Podspec.
After installed, these are merged into single module as Verge
.
To use Verge in your code, define import decralation following.
import Verge
SwiftPM
Verge supports also SwiftPM.
Questions
Please feel free to ask something about this library!
I can reply to you faster in Twitter.
ๆฅๆฌ่ชใงใฎ่ณชๅใๅ
จ็ถใชใผใฑใผใงใ
Twitterใใใ ใจๆฉใๅ็ญใงใใพใ
Demo applications
This repo has several demo applications in Demo directory. And we're looking for your demo applications to list it here! Please tell us from Issue!
Thanks
Author
License
Verge is released under the MIT license.