概览
什么是Elm架构
Elm架构(Elm架构)是Elm编程语言中常用的一个流行软件架构模式。它根据面向函数编程和单向数据流提供开发结构。
Elm架构由三个主要组件组成
Model
这是以不可变方式存储的包含所有所需数据的应用程序状态。模型通过消息(Msg
)进行更新。Update
这是一个函数,它接收当前模型和消息并返回一个新的模型。它负责处理消息并更新应用程序状态。View
这是一个函数,它接收当前模型并返回用户界面。视图显示模型中的数据,并通过生成新消息来响应用户操作。
Elm架构提供了应用程序逻辑与其呈现之间的清晰分隔,使代码更易于理解、模块化且易于测试。它还确保错误容错性,并防止许多与可变状态和副作用相关的问题。
什么是 Keemun?
Keemun 是一个跨平台的 Kotlin 框架,它提供了一个使用 Elm 架构模式编写共享代码的方法。
安装
CocoaPods
CocoaPods 是 Cocoa 项目的依赖管理器。您可以使用以下命令来安装它
$ gem install cocoapods
要使用 CocoaPods 将 Keemun 集成到您的 Xcode 项目中,请在您的 Podfile
中指定它
pod 'Keemun', '1.0.0'
然后,运行以下命令
$ pod install
Swift Package Manager
Swift Package Manager 是一个自动化 Swift 代码分布的工具,它集成到 swift
编译器中。
一旦您已设置好 Swift 包,将 Keemun 添加为依赖项就像将其添加到您的 Package.swift
的 dependencies
值一样简单。
dependencies: [
.package(url: "https://github.com/pavelannin/Keemun-Swift.git", from: "1.0.0")
]
组件
状态
状态
类描述在某个特定时刻,您的应用程序/特性/屏幕/视图等的状态。
启动
启动
是初始化的地方。它的方法返回初始化的 状态
及初始的副作用集合。
消息
任何 消息
都表示改变状态的意图。消息
由用户界面/业务逻辑发送。
更新
更新
是放置逻辑的地方。它的方法将 状态
和 消息
作为参数,并返回新的 状态
和一系列 副作用
。其想法是在一个纯函数中修改 状态
,并根据收到的 消息
以及当前 状态
发送一个副作用。
Effect
任何 Effect
都代表调用您业务逻辑部分的一个意图。根据接收到的消息,Effect
由更新发送。
EffectHandler
EffectHandler
是执行业务逻辑的位置。它的方法接受 effect
和 dispatch
作为参数。想法是作为一个函数,根据接收到的效果向 dispatch
发送消息并执行操作。EffectHandler
是一个挂起函数,每个效果都在单独的协程中执行。
Connector
Connector
是一个实体,它包含 Store
的实例。
StoreParams
StoreParams
是一个容器,它在一个地方持有 Start
、Update
和 EffectHandler
以创建一个 Store
。它为创建它提供了多个方便的重载函数,带有可选参数。
FeatureParams
FeatureParams
是一个容器,包含 StoreParams
、将 State
转换为 ViewState
的函数以及其他创建 Connector
所需的参数。 FeatureParams
提供了几个便捷的重载函数,可以使用可选参数创建它。
示例
创建 StoreParams
struct CounterStoreParams: StoreParams, MsgSplitable {
typealias Msg = SplitMsg<ExternalMsg, InternalMsg>
func start() -> Start<Self> {
.next(
.init(
syncCount: 0,
asyncCount: 0,
isAsyncRunning: false
)
)
}
static func externalUpdate(for msg: ExternalMsg, state: State) -> Update<Self> {
switch msg {
case .incrementSync:
return .next(state) { $0.syncCount = $0.syncCount + 1 }
case .decrementSync:
return .next(state) { $0.syncCount = $0.syncCount - 1 }
case .incrementAsync:
return .next(state, effect: .increment(state.asyncCount)) { $0.isAsyncRunning = true }
case .decrementAsync:
return .next(state, effect: .decrement(state.asyncCount)) { $0.isAsyncRunning = true }
}
}
static func internalUpdate(for msg: InternalMsg, state: State) -> Update<Self> {
switch msg {
case .completedAsyncOperation(let newValue):
return .next(state) {
$0.asyncCount = newValue
$0.isAsyncRunning = false
}
}
}
func effectHandler(for effect: Effect, dispatch: @escaping InternalDispatch) async {
switch effect {
case .increment(let value):
try! await Task.sleep(for: .seconds(1))
dispatch(.completedAsyncOperation(value + 1))
case .decrement(let value):
try! await Task.sleep(for: .seconds(1))
dispatch(.completedAsyncOperation(value - 1))
}
}
struct State {
var syncCount: Int
var asyncCount: Int
var isAsyncRunning: Bool
}
enum ExternalMsg {
case incrementSync
case decrementSync
case incrementAsync
case decrementAsync
}
enum InternalMsg{
case completedAsyncOperation(Int)
}
enum Effect {
case increment(Int)
case decrement(Int)
}
}
创建 FeatureParams
struct CounterFeatureParams: FeatureParams {
typealias SParams = CounterStoreParams
func stateTransform(_ state: CounterStoreParams.State) -> ViewState {
ViewState(
syncCount: String(state.syncCount),
asyncCount: String(state.asyncCount),
isAsyncRunning: state.isAsyncRunning
)
}
struct ViewState {
let syncCount: String
let asyncCount: String
let isAsyncRunning: Bool
}
}
在 UI 层的使用
struct CounterFeatureView: View {
@ObservedObject private var connector: KeemunConnector<CounterFeatureParams>
init(_ connector: KeemunConnector<CounterFeatureParams>) {
self.connector = connector
}
var body: some View {
MainView(
state: connector.state,
syncIncrementAction: { connector.dispatch(.incrementSync) },
syncDecrementAction: { connector.dispatch(.decrementSync) },
asyncIncrementAction: { connector.dispatch(.incrementAsync) },
asyncDecrementAction: { connector.dispatch(.decrementAsync) }
)
}
}
private struct MainView: View {...}
创建连接器实例
let connector = CounterFeatureParams().makeConnector(CounterStoreParams())
CounterFeatureView(connector)
示例项目
示例项目是一个带有两个计数器的屏幕:同步和异步。同步计数器在 Update
中修改,演示状态变化,而异步计数器在 EffectHandler
中修改,模拟异步业务逻辑。