iOS Swift Redux 架构详解

iOS Swift Redux 架构详解

Redux 是一种可预测的状态管理架构,非常适合复杂 iOS 应用的状态管理。下面我将全面介绍如何在 Swift 中实现 Redux 模式。

1. Redux 核心概念

Redux 基于三个基本原则:

  1. 单一数据源:整个应用状态存储在一个 Store 中
  2. 状态只读:只能通过派发 Action 来改变状态
  3. 纯函数修改:使用 Reducer 纯函数处理状态变更

Redux 数据流

sql 复制代码
Action → Store → Reducer → New State → View Update

2. 基础实现

核心协议定义

swift 复制代码
protocol Action {}  // 所有动作的标记协议

protocol State {}  // 所有状态的标记协议

protocol Reducer {
    associatedtype S: State
    func reduce(state: S, action: Action) -> S
}

Store 实现

swift 复制代码
class Store<S: State, R: Reducer>: ObservableObject where R.S == S {
    @Published private(set) var state: S
    private let reducer: R
    private let middlewares: [Middleware]
    
    init(initialState: S, reducer: R, middlewares: [Middleware] = []) {
        self.state = initialState
        self.reducer = reducer
        self.middlewares = middlewares
    }
    
    func dispatch(action: Action) {
        // 中间件处理
        let dispatchFunction: (Action) -> Void = { [weak self] action in
            self?.realDispatch(action: action)
        }
        
        var currentDispatch: (Action) -> Void = dispatchFunction
        for middleware in middlewares.reversed() {
            currentDispatch = middleware.create(store: self, dispatch: currentDispatch)
        }
        
        currentDispatch(action)
    }
    
    private func realDispatch(action: Action) {
        DispatchQueue.main.async {
            self.state = self.reducer.reduce(state: self.state, action: action)
        }
    }
}

3. 完整示例:计数器应用

定义 Action

swift 复制代码
enum CounterAction: Action {
    case increment
    case decrement
    case incrementBy(Int)
    case reset
}

定义 State

swift 复制代码
struct CounterState: State {
    var count: Int = 0
    var lastAction: String = "None"
}

实现 Reducer

swift 复制代码
struct CounterReducer: Reducer {
    func reduce(state: CounterState, action: Action) -> CounterState {
        var newState = state
        
        switch action {
        case let action as CounterAction:
            switch action {
            case .increment:
                newState.count += 1
                newState.lastAction = "Incremented by 1"
            case .decrement:
                newState.count -= 1
                newState.lastAction = "Decremented by 1"
            case .incrementBy(let amount):
                newState.count += amount
                newState.lastAction = "Incremented by \(amount)"
            case .reset:
                newState.count = 0
                newState.lastAction = "Reset to 0"
            }
        default:
            break
        }
        
        return newState
    }
}

在 SwiftUI 中使用

swift 复制代码
struct CounterView: View {
    @EnvironmentObject var store: Store<CounterState, CounterReducer>
    
    var body: some View {
        VStack(spacing: 20) {
            Text("Count: \(store.state.count)")
                .font(.largeTitle)
            
            Text("Last action: \(store.state.lastAction)")
                .font(.caption)
            
            HStack(spacing: 20) {
                Button("+") { store.dispatch(action: CounterAction.increment) }
                Button("-") { store.dispatch(action: CounterAction.decrement) }
            }
            
            Button("+10") { store.dispatch(action: CounterAction.incrementBy(10)) }
            Button("Reset") { store.dispatch(action: CounterAction.reset) }
        }
        .buttonStyle(.borderedProminent)
    }
}

// 应用入口设置
@main
struct CounterApp: App {
    let store = Store(
        initialState: CounterState(),
        reducer: CounterReducer()
    )
    
    var body: some Scene {
        WindowGroup {
            CounterView()
                .environmentObject(store)
        }
    }
}

4. 高级功能实现

中间件 (Middleware)

swift 复制代码
protocol Middleware {
    func create(store: Any, dispatch: @escaping (Action) -> Void, next: @escaping (Action) -> Void) -> (Action) -> Void
}

// 日志中间件示例
struct LoggerMiddleware: Middleware {
    func create(store: Any, dispatch: @escaping (Action) -> Void, next: @escaping (Action) -> Void) -> (Action) -> Void {
        return { action in
            print("Will dispatch: \(action)")
            next(action)
            if let store = store as? AnyStore {
                print("New state: \(store.state)")
            }
        }
    }
}

protocol AnyStore {
    var state: Any { get }
}

// 让 Store 符合 AnyStore
extension Store: AnyStore {
    var state: Any { self.state }
}

异步 Action (Side Effects)

swift 复制代码
// 异步 Action 协议
protocol AsyncAction: Action {
    func execute(dispatch: @escaping (Action) -> Void, state: () -> State?)
}

// 网络请求 Action 示例
struct FetchUserAction: AsyncAction {
    let userId: Int
    
    func execute(dispatch: @escaping (Action) -> Void, state: () -> State?) {
        DispatchQueue.global().async {
            // 模拟网络请求
            sleep(1)
            
            // 返回结果
            DispatchQueue.main.async {
                dispatch(UserFetchedAction(user: User(id: self.userId, name: "User \(self.userId)")))
            }
        }
    }
}

struct UserFetchedAction: Action {
    let user: User
}

// 扩展 Store 支持异步 Action
extension Store {
    func dispatch(asyncAction: AsyncAction) {
        asyncAction.execute(dispatch: self.dispatch, state: { self.state })
    }
}

组合 Reducer

swift 复制代码
struct CombinedReducer: Reducer {
    let reducers: [AnyReducer]
    
    init(reducers: [AnyReducer]) {
        self.reducers = reducers
    }
    
    func reduce(state: Any, action: Action) -> Any {
        return reducers.reduce(state) { currentState, reducer in
            reducer._reduce(state: currentState, action: action)
        }
    }
}

protocol AnyReducer {
    func _reduce(state: Any, action: Action) -> Any
}

struct AnyReducerWrapper<R: Reducer>: AnyReducer {
    let reducer: R
    
    func _reduce(state: Any, action: Action) -> Any {
        guard let typedState = state as? R.S else { return state }
        return reducer.reduce(state: typedState, action: action)
    }
}

5. 实际应用架构

模块化状态管理

swift 复制代码
// AppState.swift - 组合所有模块状态
struct AppState: State {
    var counter: CounterState
    var user: UserState
    var settings: SettingsState
}

// AppReducer.swift - 组合所有 Reducer
struct AppReducer: Reducer {
    func reduce(state: AppState, action: Action) -> AppState {
        var newState = state
        newState.counter = CounterReducer().reduce(state: state.counter, action: action)
        newState.user = UserReducer().reduce(state: state.user, action: action)
        newState.settings = SettingsReducer().reduce(state: state.settings, action: action)
        return newState
    }
}

使用 Selector 优化性能

swift 复制代码
struct CounterSelector {
    static func getCount(_ state: AppState) -> Int {
        return state.counter.count
    }
    
    static func getLastAction(_ state: AppState) -> String {
        return state.counter.lastAction
    }
}

// 在 View 中使用
struct CounterView: View {
    @EnvironmentObject var store: Store<AppState, AppReducer>
    
    var count: Int {
        CounterSelector.getCount(store.state)
    }
    
    var lastAction: String {
        CounterSelector.getLastAction(store.state)
    }
    
    var body: some View {
        // 使用 count 和 lastAction 而不是直接访问 store.state
    }
}

6. 与 Combine 集成

swift 复制代码
extension Store {
    func publisher<T>(_ selector: @escaping (S) -> T) -> AnyPublisher<T, Never> {
        return $state
            .map(selector)
            .removeDuplicates()
            .eraseToAnyPublisher()
    }
}

// 使用示例
class SomeViewModel: ObservableObject {
    @Published var count: Int = 0
    private var cancellables = Set<AnyCancellable>()
    
    init(store: Store<AppState, AppReducer>) {
        store.publisher(CounterSelector.getCount)
            .assign(to: \.count, on: self)
            .store(in: &cancellables)
    }
}

7. 测试策略

测试 Reducer

swift 复制代码
class CounterReducerTests: XCTestCase {
    let reducer = CounterReducer()
    
    func testIncrement() {
        let initialState = CounterState(count: 0)
        let newState = reducer.reduce(state: initialState, action: CounterAction.increment)
        XCTAssertEqual(newState.count, 1)
        XCTAssertEqual(newState.lastAction, "Incremented by 1")
    }
    
    func testDecrement() {
        let initialState = CounterState(count: 5)
        let newState = reducer.reduce(state: initialState, action: CounterAction.decrement)
        XCTAssertEqual(newState.count, 4)
    }
}

测试 Async Action

swift 复制代码
class FetchUserActionTests: XCTestCase {
    func testFetchUser() {
        let expectation = XCTestExpectation(description: "Fetch user")
        let action = FetchUserAction(userId: 1)
        
        var receivedAction: Action?
        let dispatch: (Action) -> Void = { action in
            receivedAction = action
            expectation.fulfill()
        }
        
        action.execute(dispatch: dispatch, state: { nil })
        
        wait(for: [expectation], timeout: 2)
        
        guard let userAction = receivedAction as? UserFetchedAction else {
            XCTFail("Expected UserFetchedAction")
            return
        }
        
        XCTAssertEqual(userAction.user.id, 1)
        XCTAssertEqual(userAction.user.name, "User 1")
    }
}

8. 性能优化技巧

  1. 使用 Equatable 状态 :实现状态对象的 Equatable 协议避免不必要的视图更新
  2. 细粒度订阅:只订阅需要的状态部分而非整个状态树
  3. 使用值类型状态:结构体比类更适合作为状态类型
  4. 延迟状态更新:对高频操作进行防抖处理
  5. 后台处理:将复杂计算移到后台队列,只将结果派发到主线程

9. 与其他架构对比

特性 Redux MVVM VIPER
数据流 单向 双向 单向
状态管理 集中式 分布式 分布式
复杂性 中等
可测试性
适合规模 中大型 中小型 大型
学习曲线 中等

10. 推荐库

  1. ReSwift: 最接近 JavaScript Redux 的实现
  2. TCA (The Composable Architecture): 更强大的 Redux 变体,来自 Point-Free
  3. ReduxKit: 轻量级 Redux 实现
  4. SwiftUIFlux: 专为 SwiftUI 设计的 Flux/Redux 实现

Redux 架构为 iOS 应用提供了可预测的状态管理方案,特别适合状态复杂、多人协作的大型项目。通过严格的单向数据流和不可变状态,可以显著减少难以追踪的 bug,提高代码的可维护性和可测试性。

from: deepseek
相关推荐
我不吃饼干6 分钟前
TypeScript 类型体操练习笔记(三)
前端·typescript
华仔啊9 分钟前
除了防抖和节流,还有哪些 JS 性能优化手段?
前端·javascript·vue.js
CHU72903517 分钟前
随时随地学新知——线上网课教学小程序前端功能详解
前端·小程序
清粥油条可乐炸鸡18 分钟前
motion入门教程
前端·css·react.js
这是个栗子22 分钟前
【Vue3项目】电商前台项目(四)
前端·vue.js·pinia·表单校验·面包屑导航
tumeng071122 分钟前
springboot项目架构
spring boot·后端·架构
前端Hardy25 分钟前
Electrobun 正式登场:仅 12MB,JS 桌面开发迎来轻量化新方案!
前端·javascript·electron
树上有只程序猿26 分钟前
新世界的入场券,不再只发给程序员
前端·人工智能
没有bug.的程序员35 分钟前
撕裂微服务网关的认证风暴:Spring Security 6.1 与 JWT 物理级免登架构大重构
java·spring·微服务·架构·security·jwt
confiself36 分钟前
deer-flow前端分析
前端