You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

控制器中泛型类属性注入及iOS类Redux状态管理框架化问询

Hey there! Let's break down your two questions one by one with practical, Swift-focused solutions tailored for iOS development:

1. 属性注入泛型类到控制器的实现方案

Injecting generic classes into view controllers requires balancing type safety and flexibility. Here are three reliable approaches:

方案一:构造器注入(最推荐,符合依赖倒置原则)

This is the cleanest approach—it makes dependencies explicit and simplifies testing.

Example generic service class:

class GenericService<T> {
    func fetchData() -> T? {
        // Generic data fetch logic
        return nil
    }
}

Inject via custom initializer in your view controller:

class MyViewController: UIViewController {
    private let service: GenericService<MyDataModel>
    
    init(service: GenericService<MyDataModel>) {
        self.service = service
        super.init(nibName: nil, bundle: nil)
    }
    
    required init?(coder: NSCoder) {
        fatalError("Use the custom initializer instead")
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        if let data = service.fetchData() {
            // Handle fetched data
        }
    }
}

If you need to support Storyboards/XIBs, you can combine this with a setup method called after initialization, but constructor injection remains the priority for clarity.

方案二:协议驱动的属性注入

For scenarios where constructor injection isn't feasible (e.g., Storyboard instantiation), use a protocol to abstract generic functionality:

Define a protocol for your generic service:

protocol DataFetching {
    associatedtype DataType
    func fetchData() -> DataType?
}

extension GenericService: DataFetching {}

Inject via property in the view controller:

class MyViewController: UIViewController {
    var dataFetcher: DataFetching where DataType == MyDataModel?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        if let data = dataFetcher.fetchData() {
            // Handle data
        }
    }
}

Assign the service when navigating to the controller:

let service = GenericService<MyDataModel>()
let vc = MyViewController()
vc.dataFetcher = service
navigationController?.pushViewController(vc, animated: true)

方案三:依赖注入容器(适合 complex projects)

For large apps with many generic dependencies, use a DI container to centralize management:

class DIContainer {
    static let shared = DIContainer()
    private var registrations: [String: Any] = []
    
    private init() {}
    
    func register<T>(_ instance: T) {
        let key = String(describing: T.self)
        registrations[key] = instance
    }
    
    func resolve<T>() -> T? {
        let key = String(describing: T.self)
        return registrations[key] as? T
    }
}

Register your generic service at app launch:

DIContainer.shared.register(GenericService<MyDataModel>())

Resolve it in your view controller:

class MyViewController: UIViewController {
    private let service: GenericService<MyDataModel>
    
    override init(nibName: String?, bundle: Bundle?) {
        guard let service = DIContainer.shared.resolve() else {
            fatalError("Failed to resolve GenericService")
        }
        self.service = service
        super.init(nibName: nibName, bundle: bundle)
    }
    
    // ... rest of the code
}

2. iOS中Redux-like状态管理框架的抽象解耦与Store注入方案

Great job building a working Redux-like solution! Abstracting it into a reusable framework will absolutely boost reusability and testability. Let's structure this step by step:

Step 1: Abstract the Core Store Class

First, create a generic Store class that accepts custom State and Reducer from the user:

// Base protocol for all user-defined actions
public protocol Action {}

// Reducer type: takes current State and Action, returns new State
public typealias Reducer<State> = (State, Action) -> State

public class Store<State> {
    private var currentState: State
    private let reducer: Reducer<State>
    private let queue = DispatchQueue(label: "com.yourframework.store.queue")
    private var subscribers: [(State) -> Void] = []
    
    public init(initialState: State, reducer: @escaping Reducer<State>) {
        self.currentState = initialState
        self.reducer = reducer
    }
    
    // Thread-safe state access
    public func getState() -> State {
        queue.sync { currentState }
    }
    
    // Thread-safe action dispatch
    public func dispatch(_ action: Action) {
        queue.async { [weak self] in
            guard let self = self else { return }
            let newState = self.reducer(self.currentState, action)
            self.currentState = newState
            // Notify all subscribers of state change
            self.subscribers.forEach { $0(newState) }
        }
    }
    
    // Subscribe to state updates
    public func subscribe(_ subscriber: @escaping (State) -> Void) {
        queue.async { [weak self] in
            guard let self = self else { return }
            self.subscribers.append(subscriber)
            // Send current state to new subscriber immediately
            subscriber(self.currentState)
        }
    }
}

This generic Store lets users plug in their own State structs and Reducers without modifying the framework core.

Step 2: Implement Store Injection into Any Class

Here are three flexible ways to inject the Store into classes like UIViewControllers or UIViews:

方式一:协议注入(最适合测试)

Define a protocol for classes that need access to the Store, then inject via constructor or property:

public protocol StoreHolder {
    associatedtype StateType
    var store: Store<StateType> { get }
}

// Example view controller using the protocol
class MyViewController: UIViewController, StoreHolder {
    typealias StateType = AppState
    let store: Store<AppState>
    
    init(store: Store<AppState>) {
        self.store = store
        super.init(nibName: nil, bundle: nil)
    }
    
    required init?(coder: NSCoder) {
        fatalError("Use custom initializer")
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Access current state
        let currentState = store.getState()
        // Subscribe to state changes
        store.subscribe { [weak self] newState in
            DispatchQueue.main.async {
                self?.updateUI(with: newState)
            }
        }
    }
    
    private func updateUI(with state: AppState) {
        // Update UI based on new state
    }
}

This approach makes testing easy—you can pass a mock Store in unit tests.

方式二:属性包装器(语法糖简化代码)

Use a Swift property wrapper to reduce boilerplate when injecting the Store:

@propertyWrapper
public struct InjectStore<State> {
    private let store: Store<State>
    
    public init() {
        guard let store = DIContainer.shared.resolve() else {
            fatalError("Store not registered in DI container")
        }
        self.store = store
    }
    
    public var wrappedValue: Store<State> { store }
}

Use it in your view controller:

class MyViewController: UIViewController {
    @InjectStore var store: Store<AppState>
    
    override func viewDidLoad() {
        super.viewDidLoad()
        let state = store.getState()
        // ... use store here
    }
}

Remember to register your Store in the DI container at app launch:

let initialAppState = AppState()
let appStore = Store(initialState: initialAppState, reducer: appReducer)
DIContainer.shared.register(appStore)

方式三:全局 Store(简单但谨慎使用)

For small projects, a global Store is quick to implement, but note it can make testing harder:

// Global variable in your framework
public var SharedStore: Store<AppState>?

// Initialize at app launch
SharedStore = Store(initialState: AppState(), reducer: appReducer)

// Use in view controller
class MyViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        guard let store = SharedStore else { return }
        // ... use store here
    }
}

Step 3: Boost Reusability & Testability

  • Modularize the framework: Split Action, Reducer, and Store into separate modules so users only import what they need.
  • Add testing tools: Create a MockStore class to help users test state changes and action dispatches:
public class MockStore<State>: Store<State> {
    public var dispatchedActions: [Action] = []
    
    override public func dispatch(_ action: Action) {
        dispatchedActions.append(action)
        // Optional: manually control state updates for testing
    }
}
  • Include examples: Add sample code showing how to define a custom State, write a Reducer, and inject the Store into a view controller.

内容的提问来源于stack exchange,提问作者Fabien Gavory

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 04:01:38