#13: @Ben-G 已经独立开发了一个非常严谨、非常Swift风格的 Redux 实现作为 Swift-Flow。
经过短暂讨论,双方都同意合作和共享想法将对两个项目都有益处。去看看吧。欢迎对这两个项目的反馈。
ReduxKit 是 Dan Abramov 和 React 社区使用的 JavaScript Redux 库的 Swift 实现。ReduxKit 尽可能地接近 Redux,同时适当地引入 Swift 的做事方式。
有关框架的详细说明可以在官方 Redux 仓库中找到: Redux。
ReduxKit 目前已在一些 Swift 应用程序中实现,并且经常更新。非常期待添加、中间件和帮助!所以如果您正在尝试它并且有任何建议 - 随时欢迎提出问题,我会及时回复。
原始 gist 的一个更高级的 Swift 示例
import ReduxKit
/**
* This is a simple standard action. The only requirement is that an action complies to
* the Action protocol. The SimpleStandardAction contains a strongly typed rawPayload
* property. The protocol automatically assigns the rawPayload to the Actions payload
* property. This removes the necessity of type casting whenever working with actions in
* a reducer.
*
* There's also the StandardAction protocol, that requires the struct to have an
* initializer. This is required if the bindActionCreators helper is to be used.
*/
struct IncrementAction: SimpleStandardAction {
let meta: Any? = nil
let error: Bool = false
let rawPayload: Int = 1
}
struct DecrementAction: SimpleStandardAction {
let meta: Any? = nil
let error: Bool = false
let rawPayload: Int = 1
}
/**
* This is a simple reducer. It is a pure function that follows the syntax
* (state, action) -> state.
* It describes how an action transforms the previous state into the next state.
*
* Instead of using the actions.type property - as is done in the regular Redux framework
* we use the power of Swifts static typing to deduce the action.
*/
func counterReducer(previousState: Int?, action: Action) -> Int {
// Declare the reducers default value
let defaultValue = 0
var state = previousState ?? defaultValue
switch action {
case let action as IncrementAction:
return state + action.rawPayload
case let action as DecrementAction:
return state - action.rawPayload
default:
return state
}
}
/**
* The applications state. This should contain the state of the whole application.
* When building larger applications, you can optionally assign complex structs to
* properties on the AppState and handle them in the part of the application that
* uses them.
*/
struct AppState {
var count: Int!
}
/**
* Create the applications reducer. While we could create a combineReducer function
* we've currently chosen to allow reducers to be statically typed and accept
* static states - instead of Any - which currently forces us to define the
* application reducer as such. This could possibly be simplified with reflection.
*/
let applicationReducer = {(state: AppState? = nil, action: Action) -> AppState in
return AppState(
count: counterReducer(state?.count, action: action),
)
}
// Create application store. The second parameter is an optional default state.
let store = createStore(applicationReducer, nil)
let disposable = store.subscribe{ state in
print(state)
}
store.dispatch(IncrementAction())
// {counter: 1}
store.dispatch(IncrementAction())
// {counter: 2}
store.dispatch(DecrementAction())
// {counter: 1}
// Dispose of the subscriber after use.
disposable.dispose()
ReduxKit 在简单的实现和使用方面大量使用了 Swift 泛型。
作为泛型使用的类型只有 State
、Action
和 PayloadType
。其中只有 State
没有推断,并且会被频繁使用。由于 typealias
不支持泛型,这会导致框架源代码更难阅读。示例 typealias
已在源代码中内部包含。
一旦在您的项目中定义了根状态类型,您可能需要声明适当的 typealias
以映射到 JavaScript Redux 类型。
// These two are already exported by ReduxKit as they do not use the State generic
// typealias Dispatch = Action -> Action
// typealias DispatchTransformer = Dispatch -> Dispatch
struct State {} // Can be named anything you like, as long as it's consistent in the typealias declarations
// Underscores are used where needed to prevent clashes with exported protocols. Again, naming is up to you.
typealias Store = ReduxKit.Store<State>
typealias Reducer = (previousState: State?, action: Action) -> State
typealias Subscriber = (updatedState: State) -> ()
typealias MiddlewareApi = Store
typealias Middleware = MiddlewareApi -> DispatchTransformer
typealias StoreCreator = (reducer: Reducer, initialState: State?) -> Store
typealias StoreEnhancer = (StoreCreator) -> StoreCreator
typealias StateStream = ReduxKit.StateStream<State>
typealias StreamFactory = (initialState: State) -> StateStream
在 applyMiddleware
函数中可以看到不支持的泛型,这是扩展 ReduxKit 示例的最佳例子。
// How the applyMiddleware function would ideally be declared
func applyMiddleware(middleware: [Middleware])
-> StoreEnhancer
// Expanding out (sans generic state):
func applyMiddleware(middleware: [MiddlewareApi -> DispatchTransformer])
-> StoreCreator
-> StoreCreator
func applyMiddleware(middleware: [Store -> DispatchTransformer])
-> ((Reducer, State?) -> Store)
-> ((Reducer, State?) -> Store)
func applyMiddleware(middleware: [Store -> DispatchTransformer])
-> (((State?, Action) -> State, State?) -> Store)
-> (((State?, Action) -> State, State?) -> Store)
// With the generic State
func applyMiddleware<State>(middleware: [(Store<State>) -> DispatchTransformer])
-> (((State?, Action) -> State, State?) -> Store<State>)
-> (((State?, Action) -> State, State?) -> Store<State>)
简化版和实际公共函数和类型
createStore(reducer: Reducer, state: State?) -> Store
createStore<State>(reducer: (State?, Action) -> State, state: State?) -> Store<State>
createStreamStore(streamFactory: StreamFactory, reducer: Reducer, state: State?) -> Store
createStreamStore<State>(
streamFactory: State -> StateStream<State> = createSimpleStream,
reducer: (State?, Action) -> State,
state: State?)
-> Store<State>
createStreamStore(streamFactory: StreamFactory) -> StoreCreator
createStreamStore<State>(streamFactory: State -> StateStream<State>)
-> (reducer: (State?, Action) -> State, state: State?)
-> Store<State>
protocol ReduxDisposable {
var disposed: Bool { get }
var dispose: () -> () { get }
}
struct SimpleReduxDisposable: ReduxDisposable {
init(disposed: () -> Bool, dispose: () -> ())
}
struct StateStream {
let dispatch: State -> ()
let subscribe: Subscriber -> ReduxDisposable
let getState: () -> State
init(dispatch: State -> (), subscribe: Subscriber -> ReduxDisposable, getState: () -> State)
}
struct StateStream<State> {
let dispatch: State -> ()
let subscribe: (State -> ()) -> ReduxDisposable
let getState: () -> State
init(dispatch: State -> (), subscribe: (State -> ()) -> ReduxDisposable, getState: () -> State)
}
protocol StoreType {
typealias State
var dispatch: Dispatch { get }
var subscribe: (Subscriber) -> ReduxDisposable { get }
var getState: () -> State { get }
init(dispatch: Dispatch, subscribe: (Subscriber) -> ReduxDisposable, getState: () -> State)
}
struct Store<State>: StoreType {
let dispatch: Dispatch
let subscribe: (Subscriber) -> ReduxDisposable
let getState: () -> State
var state: State
init(dispatch: Dispatch, subscribe: (Subscriber) -> ReduxDisposable, getState: () -> State)
}
func applyMiddleware(middleware: [Middleware]) -> StoreEnhancer
func applyMiddleware<State>(middleware: [Store<State> -> DispatchTransformer])
-> (((State?, Action) -> State, State?) -> Store<State>)
-> (((State?, Action) -> State, State?) -> Store<State>)
func bindActionCreators<Action where Action: StandardAction>(type: Action.Type, dispatch: Dispatch)
-> (payload: Action.PayloadType?)
-> ()
ReduxKit 存储在本质上,是一种可订阅的状态流。你可能已经从响应式框架中熟悉了这个概念。事实上,这两者非常相似,你可能会更愿意使用 ReduxKit 与你喜欢的响应式框架一起使用。
状态流类型正好允许这样做。结合状态流工厂,它允许 ReduxKit 使用几乎任何现有响应式框架作为状态流。
ReduxKit 中包含了一个状态流。不推荐将其用于任何需要更多的情况,因为它不是线程安全或泄漏安全的。SimpleStateStream 只是一个订阅者数组。
ReduxKit 为以下提供了状态流绑定:
待办事项:包括示例和链接到响应式流提供程序。
我希望 ReduxKit 的中间件能够得到顺利的实施,以便我们能在将来改变我们做 iOS 开发的方式。因此,我欢迎使用拉取请求、功能请求和建议。我希望这个库能变得尽可能好,而我只有通过大家的帮助才能实现这一点。
ReduxKit 的实际实现相当完整。但仍然需要更多的工具。
MIT