-
Notifications
You must be signed in to change notification settings - Fork 3
Usage
In common / shared module's build.gradle.kts
kotlin {
sourceSets {
val commonMain by getting {
dependencies {
implementation("com.msabhi:flywheel:1.1.5-RC")
}
}
}
}
Flywheel supports all the available platforms when used as a dependency for a Kotlin Multiplatform project, since the core of the Flywheel is defined in the common module itself, there are no platform-specific core implementations. Flywheel uses the multithreaded version of kotlinx.coroutines
. Even though it is production-ready, care should be taken to handle threading properly in non-JVM platforms.
In app's build.gradle.kts
dependencies {
implementation("com.msabhi:flywheel-android:1.1.5-RC")
}
- We recommend using Flywheel with
ViewModel
. - The
StateReserve
can be scoped to ViewModel's lifecycle. But, don't use theviewModelScope
, since it usesDispatchers.Main
. Provide your ownCoroutineScope
that usesDispatchers.Default + SupervisorJob()
For ease of use, you can use thegetDefaultScope()
provided with Flywheel. - You should call
stateReserve.terminate()
in viewModels'sonCleared()
, which cancels the scope. - Flywheel's
StatReserve
can also be used at the application level for use-cases like maintaining the user's authentication state, app configuration, etc...
Please check the provided sample counter app for a better understanding.
Almost similar to Android
. You can use Flywheel with any architecture that fits your application.
Usually, the Kotlin Multiplatform approach is used in combination with a Kotlin Multiplatform project where Flywheel will be added as a common module's dependency. Still, Flywheel can be used as a standalone framework with Apple platforms using Swift Package Manager
.
You can use the Swift Package Manager to install Flywheel
by adding the proper description to your Package.swift
file:
// swift-tools-version:5.3
import PackageDescription
let package = Package(
name: "YOUR_PROJECT_NAME",
dependencies: [
.package(url: "https://github.com/abhimuktheeswarar/Flywheel.git", from: "1.1.5-RC"),
]
)
Then run swift build
whenever you get prepared.
To help creating StateReserve
easier from Swift, we have provided a helper class StateReserveHolder
open class StateReserveHolder<S : State>(
initialState: S,
reduce: Reduce<S>,
config: StateReserveConfig?,
)
This is just an optional wrapper, feel free to use the StateReserve
directly.
import SwiftUI
import flywheel
struct ContentView: View {
@ObservedObject var viewModel = CounterViewModel()
var body: some View {
VStack(alignment: .center) {
HStack( content: {
Spacer()
Button("DECREMENT") {viewModel.decrement()}
Spacer()
Text("\(viewModel.state.count)")
Spacer()
Button("INCREMENT") {viewModel.increment()}
Spacer()
})
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
class CounterState : Equatable,Hashable {
var count : Int
init() {
count = 0
}
static func == (lhs: CounterState, rhs: CounterState) -> Bool {
debugPrint("equality check")
return lhs.count == rhs.count
}
func hash(into hasher: inout Hasher) {
hasher.combine(count)
}
}
class CounterIncrementAction : Action {}
class CounterDecrementAction : Action {}
class CounterResetAction : Action {}
class CounterViewModel : ObservableObject {
private lazy var stateReserveHolder = StateReserveHolder(initialState: CounterState(), reduce: reduce.self, config: nil)
@Published var state : CounterState = CounterState()
init() {
stateReserveHolder.states.collect(collector: Collector<CounterState> { counterState in
self.state = counterState
}) { (unit, error) in
// code which is executed if the Flow object completed
}
stateReserveHolder.actions.collect(collector: Collector<Action> { action in
debugPrint("action = \(action) | state = \(self.state.count)")
if(self.state.count == -10) {
self.dispatch(action: CounterResetAction())
}
}) { (unit, error) in
}
CounterSideEffect.init(stateReserve: stateReserveHolder.stateReserve, dispatchers: DispatcherProviderImpl.init())
}
func reduce(action:Action,currentState :CounterState) -> CounterState {
let newState = currentState
switch action {
case _ as CounterIncrementAction:
newState.count += 1
case _ as CounterDecrementAction:
newState.count -= 1
case _ as CounterResetAction:
newState.count = 0
default:
break
}
return newState
}
private func dispatch(action : Action) {
stateReserveHolder.dispatch(action: action)
}
func increment() {
dispatch(action: CounterIncrementAction())
}
func decrement() {
dispatch(action: CounterDecrementAction())
}
}
class CounterSideEffect: SideEffect<CounterState> {
override init(stateReserve: StateReserve<CounterState>, dispatchers: DispatcherProvider) {
super.init(stateReserve: stateReserve, dispatchers: dispatchers)
stateReserve.actionStates.collect(collector: Collector<ActionStateAlways<Action,CounterState>> { actionState in
self.handle(action: actionState.action, state: actionState.state)
}) { (unit, error) in
// code which is executed if the Flow object completed
}
}
func handle(action: Action, state :CounterState) {
debugPrint("count = \(state.count)")
if(state.count == 10) {
self.dispatch(action: CounterResetAction())
}
}
}
Please look at the CoroutineHelper
file provided in the sample iOS app module, which provides wrappers for collecting a Flow<S>
in Swift.
Though, we don't want to compete with Redux
. Flywheel will be provided as a standalone npm package
soon. As of now, Add Flywheel as a dependency to your common/shared module in your Kotlin Multiplatform project.
Add Flywheel as a dependency to your common/shared module in your Kotlin Multiplatform project.