RecoilSwift
RecoilSwift is a lightweight & reactive swift state management library. RecoilSwift is a SwiftUI implementation of recoil.js which powered by Facebook.
Recoil is an alternate option to replace of the Redux(reswift)
or MVVM
.
What is recoil
Requirements
- iOS 13+
- Xcode 12.4+
NOTE: Currently this library only support for SwiftUI, UIKit is not available. But it planned.
In recent release, we re-implement this library with react hooks pattern which making the usage of this lib is more similar with official way.
Installation
- In Xcode, open your project and navigate to File → Swift Packages → Add Package Dependency...
- Paste the repository URL (
https://github.com/hollyoops/RecoilSwift.git
) and click Next. - For Rules, select Branch (with branch set to
master
). - Click Finish.
RecoilSwift is available through CocoaPods. To install it, simply add the following line to your Podfile:
pod 'RecoilSwift'
State Management Data Flow
← ← ← ← ← ← ← ← ← atoms ← ← ← ← ← ← ← ←
↓ ↑
↓ ↑
selectors set / writeable selectors
↓ ↑
↓ ↑
→ → → → → → → view(hooks) → → → → → → →
Basic Usage
Create Atom / Selector:
// Create a Atom
let allBooksState = atom { [Book]() }
// Create readonly Selector
let currentBooksSelector = selector { get -> [Book] in
let books = get(allBooksState)
if let category = get(selectedCategoryState) {
return books.filter { $0.category == category }
}
return books
}
// Create parameterized selector
let someSelector = selectorFamily { (id: String, get: ReadonlyContext) -> AnyPublisher<[String], Error> in
// Do some logic in here with id
}
Use Atom / Selector in SwiftUI
Because the useRecoilXXX
series API is based on Hooks. so it should follow all the rule of hooks
struct YourView: RecoilView { // You have to implement the RecoilView protocol
var hookBody: some View {
let currentBooks = useRecoilValue(currentBooksSelector)
let allBooks = useRecoilState(allBooksStore)
let loadable = useRecoilValueLoadable(fetchRemoteDataByID(someID))
// Your UI Code
}
}
Advance Usage
Async task
let fetchRemoteDataById = selectorFamily { (id: String, get: ReadonlyContext) -> AnyPublisher<[String], Error> in
Deferred {
Future { promise in
// Do some logic in here with id
}
}.eraseToAnyPublisher()
}
// In some function
func someView() -> some View {
HookScope { // when your view is not implement with RecoilView, you have to use `HookScope`
let id = useRecoilValue(selectedCategoryState)
let loadable = useRecoilValueLoadable(fetchRemoteDataById(id))
// This body will be render after task completed
return VStack {
// while loading
if loadable.isLoading {
ProgressView()
}
// when error
if let err = loadable.error {
errorView(err)
}
// when data fulfill
if let names = loadable.data {
dataView(allBook: names, onRetry: loadable.retry)
}
}
}
}
Documentation
API Reference
-
State
-
Utils & Hooks
TODOs
- [feature]Add
refresh
for loadable - [performance]Fix circular reference for selector
- [performance]Caches value for selector
- [feature]Make UIKit compatible
Reference:
-
Facebook Recoil (Recoil.js)
-
Recoil for Android
-
Hooks