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
相关推荐
安冬的码畜日常1 分钟前
【AI 加持下的 Python 编程实战 2_10】DIY 拓展:从扫雷小游戏开发再探问题分解与 AI 代码调试能力(中)
开发语言·前端·人工智能·ai·扫雷游戏·ai辅助编程·辅助编程
小杨升级打怪中6 分钟前
前端面经-JS篇(三)--事件、性能优化、防抖与节流
前端·javascript·xss
清风细雨_林木木10 分钟前
Vue开发网站会有“#”原因是前端路由使用了 Hash 模式
前端·vue.js·哈希算法
鸿蒙布道师33 分钟前
OpenAI为何觊觎Chrome?AI时代浏览器争夺战背后的深层逻辑
前端·人工智能·chrome·深度学习·opencv·自然语言处理·chatgpt
袈裟和尚38 分钟前
如何在安卓平板上下载安装Google Chrome【轻松安装】
前端·chrome·电脑
曹牧41 分钟前
HTML字符实体和转义字符串
前端·html
小希爸爸1 小时前
2、中医基础入门和养生
前端·后端
局外人LZ1 小时前
前端项目搭建集锦:vite、vue、react、antd、vant、ts、sass、eslint、prettier、浏览器扩展,开箱即用,附带项目搭建教程
前端·vue.js·react.js
KdanMin1 小时前
AR行业应用案例与NXP架构的结合
架构·ar
G_GreenHand1 小时前
Dhtmlx Gantt教程
前端