Swift中不使用ReSwift实现跨页面状态更新监听的方法
Hey there! I see you're using ReSwift for cross-page state sync (where your second screen listens to actions from the first, updating its counter and language values), and you want to explore native/non-framework alternatives. Let's go through several solid approaches that work natively in Swift/iOS:
1. NotificationCenter (Observer Pattern)
This is the most straightforward iOS-native solution, perfect for simple event broadcasting across unrelated screens. It uses the observer pattern to send and receive state updates.
Implementation Steps:
First, define a custom notification name to avoid conflicts:
extension Notification.Name { static let appStateUpdated = Notification.Name("AppStateUpdated") }
In your first view controller, trigger a notification with updated state data when the button is tapped:
@IBAction func updateButtonTapped(_ sender: UIButton) { // Example updated state values let updatedState = ["counter": 3, "language": "English"] NotificationCenter.default.post( name: .appStateUpdated, object: nil, userInfo: updatedState ) }
In your second view controller, register to listen for the notification and update the UI when it arrives:
override func viewDidLoad() { super.viewDidLoad() // Set initial state counterLabel.text = "2" languageLabel.text = "Chinese" // Register notification observer NotificationCenter.default.addObserver( self, selector: #selector(handleStateUpdate(_:)), name: .appStateUpdated, object: nil ) } @objc private func handleStateUpdate(_ notification: Notification) { guard let userInfo = notification.userInfo, let counter = userInfo["counter"] as? Int, let language = userInfo["language"] as? String else { return } // Update UI with new state counterLabel.text = "\(counter)" languageLabel.text = language } // Always clean up observers to avoid memory leaks deinit { NotificationCenter.default.removeObserver(self) }
2. Combine Framework (Reactive, Type-Safe Sync)
If you're targeting iOS 13+, Apple's Combine framework offers a more elegant, type-safe alternative to NotificationCenter. It’s built for reactive state management without external dependencies.
Implementation Steps:
First, create an observable state object to hold your app's shared state:
import Combine class AppState: ObservableObject { @Published var counter: Int = 2 @Published var language: String = "Chinese" }
Pass this state object to both view controllers (you can use environment objects in SwiftUI, or direct property injection for UIKit):
// Example in SceneDelegate/WindowSceneDelegate func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { let sharedState = AppState() let firstVC = FirstViewController() firstVC.appState = sharedState let secondVC = SecondViewController() secondVC.appState = sharedState // Set up navigation and window as usual let navController = UINavigationController(rootViewController: firstVC) window?.rootViewController = navController window?.makeKeyAndVisible() }
In the first view controller, modify the shared state when the button is tapped:
class FirstViewController: UIViewController { var appState: AppState! @IBAction func updateButtonTapped(_ sender: UIButton) { appState.counter = 3 appState.language = "English" } }
In the second view controller, subscribe to state changes and update the UI automatically:
class SecondViewController: UIViewController { var appState: AppState! private var cancellables = Set<AnyCancellable>() override func viewDidLoad() { super.viewDidLoad() updateUI() // Listen for counter changes appState.$counter .receive(on: DispatchQueue.main) // Ensure UI updates run on main thread .sink { [weak self] newCounter in self?.counterLabel.text = "\(newCounter)" } .store(in: &cancellables) // Listen for language changes appState.$language .receive(on: DispatchQueue.main) .sink { [weak self] newLanguage in self?.languageLabel.text = newLanguage } .store(in: &cancellables) } private func updateUI() { counterLabel.text = "\(appState.counter)" languageLabel.text = appState.language } }
3. Singleton State Manager (Simple Shared State)
For small apps or straightforward state needs, a singleton-based state manager is lightweight and easy to implement. It centralizes state and notifies listeners when values change.
Implementation Steps:
Create a singleton manager with state properties and listener support:
class AppStateManager { static let shared = AppStateManager() private init() {} // Enforce singleton pattern var counter: Int = 2 { didSet { notifyListeners() } } var language: String = "Chinese" { didSet { notifyListeners() } } private var listeners: [UUID: () -> Void] = [:] func addListener(id: UUID, handler: @escaping () -> Void) { listeners[id] = handler } func removeListener(id: UUID) { listeners.removeValue(forKey: id) } private func notifyListeners() { listeners.values.forEach { $0() } } }
In the first view controller, update the singleton state on button tap:
@IBAction func updateButtonTapped(_ sender: UIButton) { AppStateManager.shared.counter = 3 AppStateManager.shared.language = "English" }
In the second view controller, register a listener to update the UI when state changes:
class SecondViewController: UIViewController { private let listenerId = UUID() override func viewDidLoad() { super.viewDidLoad() updateUI() // Register listener AppStateManager.shared.addListener(id: listenerId) { [weak self] in DispatchQueue.main.async { self?.updateUI() } } } private func updateUI() { counterLabel.text = "\(AppStateManager.shared.counter)" languageLabel.text = AppStateManager.shared.language } deinit { // Clean up listener to avoid memory leaks AppStateManager.shared.removeListener(id: listenerId) } }
4. Delegate Pattern (One-to-One Communication)
If your screens have a direct parent-child relationship (e.g., first screen pushes/presents the second), the delegate pattern works well for targeted, one-to-one state updates.
Implementation Steps:
Define a delegate protocol for state updates:
protocol FirstViewControllerDelegate: AnyObject { func didUpdateState(counter: Int, language: String) }
Add a delegate property to the first view controller:
class FirstViewController: UIViewController { weak var delegate: FirstViewControllerDelegate? @IBAction func updateButtonTapped(_ sender: UIButton) { delegate?.didUpdateState(counter: 3, language: "English") } }
Implement the delegate in the second view controller:
class SecondViewController: UIViewController, FirstViewControllerDelegate { override func viewDidLoad() { super.viewDidLoad() // Set initial state counterLabel.text = "2" languageLabel.text = "Chinese" } func didUpdateState(counter: Int, language: String) { counterLabel.text = "\(counter)" languageLabel.text = language } }
When presenting/pushing the second view controller from the first, set the delegate:
// In FirstViewController @IBAction func openSecondVC(_ sender: UIButton) { let secondVC = SecondViewController() self.delegate = secondVC navigationController?.pushViewController(secondVC, animated: true) }
Each approach has its sweet spot:
- NotificationCenter: Great for simple, global event broadcasting
- Combine: Best for reactive, type-safe state management (iOS 13+)
- Singleton Manager: Lightweight option for small apps
- Delegate: Ideal for direct, one-to-one screen communication
内容的提问来源于stack exchange,提问作者K.Wu

