Swift应用全局状态管理及状态变更时UI更新方案咨询
Hey there! Since you're coming from React Native + Redux, you already get the core idea of centralized state management for syncing UI across your app. Swift has a couple of solid approaches to handle exactly the scenarios you outlined—let's walk through them with concrete examples.
If you want to avoid third-party libraries, Apple's native Combine framework paired with ObservableObject (built into SwiftUI, but adaptable to UIKit too) is perfect. It works similarly to Redux's state subscription model: when your centralized state updates, all subscribed views automatically refresh.
Scenario 1: Sync Avatar Updates Across Account Pages
First, create a global, shared state manager to hold your app's core data (like user info):
import Combine class AppState: ObservableObject { // Singleton for easy global access static let shared = AppState() // @Published automatically sends updates to subscribers when this value changes @Published var currentUser: User = User(avatarURL: "default_avatar.png", username: "Guest") struct User { var avatarURL: String var username: String } }
Use in SwiftUI Views
Any view that displays the user's avatar just needs to observe the shared AppState:
struct ProfileView: View { @ObservedObject var appState = AppState.shared var body: some View { VStack { Image(appState.currentUser.avatarURL) .resizable() .frame(width: 100, height: 100) Text(appState.currentUser.username) } } }
When you update the avatar (e.g., in a settings screen), all subscribed views will refresh automatically:
struct SettingsView: View { @ObservedObject var appState = AppState.shared var body: some View { Button("Update Avatar") { // Replace with actual network response or local asset URL appState.currentUser.avatarURL = "new_avatar.png" } } }
Use in UIKit
If you're working with UIKit, you can subscribe to the @Published property using Combine:
class ProfileViewController: UIViewController { private let appState = AppState.shared private var cancellables = Set<AnyCancellable>() @IBOutlet weak var avatarImageView: UIImageView! override func viewDidLoad() { super.viewDidLoad() // Subscribe to user state changes and update the UI appState.$currentUser .sink { [weak self] user in self?.avatarImageView.image = UIImage(named: user.avatarURL) } .store(in: &cancellables) } }
Scenario 2: Sync Article Likes Across Category Pages
Extend the AppState to include your article data, then add a method to toggle likes. All views that display articles will automatically sync when the state updates.
Update the State Manager
class AppState: ObservableObject { static let shared = AppState() @Published var currentUser: User = User(avatarURL: "default_avatar.png", username: "Guest") @Published var articles: [Article] = [ Article(id: 1, title: "My Cute Dog", tags: ["Pets", "Family"], isLiked: false), Article(id: 2, title: "Weekend BBQ", tags: ["Family"], isLiked: false) ] struct User { var avatarURL: String var username: String } struct Article: Identifiable { let id: Int let title: String let tags: [String] var isLiked: Bool } // Centralized method to toggle article likes func toggleLike(for articleId: Int) { guard let index = articles.firstIndex(where: { $0.id == articleId }) else { return } articles[index].isLiked.toggle() } }
Build Category Pages
The "Pets" and "Family" pages will filter articles by tag and observe the shared state. When a like is toggled in one page, the other will update instantly:
// Pets Category Page struct PetsArticlesView: View { @ObservedObject var appState = AppState.shared private var petsArticles: [AppState.Article] { appState.articles.filter { $0.tags.contains("Pets") } } var body: some View { List(petsArticles) { article in HStack { Text(article.title) Spacer() Button(action: { appState.toggleLike(for: article.id) }) { Image(systemName: article.isLiked ? "heart.fill" : "heart") .foregroundColor(article.isLiked ? .red : .gray) } } } } } // Family Category Page (nearly identical) struct FamilyArticlesView: View { @ObservedObject var appState = AppState.shared private var familyArticles: [AppState.Article] { appState.articles.filter { $0.tags.contains("Family") } } var body: some View { List(familyArticles) { article in HStack { Text(article.title) Spacer() Button(action: { appState.toggleLike(for: article.id) }) { Image(systemName: article.isLiked ? "heart.fill" : "heart") .foregroundColor(article.isLiked ? .red : .gray) } } } } }
If you want to stick closely to Redux's action-reducer-store pattern, use ReSwift (a popular third-party library for Redux-style state management in Swift).
Setup
First, define your state, actions, and reducer:
import ReSwift // App State struct AppState: StateType { var currentUser: User var articles: [Article] struct User { var avatarURL: String var username: String } struct Article: Identifiable { let id: Int let title: String let tags: [String] var isLiked: Bool } } // Actions enum AppAction: Action { case updateAvatar(url: String) case toggleArticleLike(articleId: Int) } // Reducer func appReducer(action: Action, state: AppState?) -> AppState { var state = state ?? AppState( currentUser: AppState.User(avatarURL: "default_avatar.png", username: "Guest"), articles: [ AppState.Article(id: 1, title: "My Cute Dog", tags: ["Pets", "Family"], isLiked: false), AppState.Article(id: 2, title: "Weekend BBQ", tags: ["Family"], isLiked: false) ] ) switch action as? AppAction { case .updateAvatar(let url): state.currentUser.avatarURL = url case .toggleArticleLike(let articleId): if let index = state.articles.firstIndex(where: { $0.id == articleId }) { state.articles[index].isLiked.toggle() } case .none: break } return state } // Create the store let store = Store( reducer: appReducer, state: nil )
Subscribe to the Store in SwiftUI
Create a helper ObservableObject to bridge ReSwift's store to SwiftUI:
class StoreSubscriber<T: StateType>: ObservableObject { @Published var state: T private var cancellable: AnyCancellable? init(store: Store<T>) { self.state = store.state cancellable = store.publisher.sink { [weak self] newState in self?.state = newState } } } // Use in a view struct ProfileView: View { @ObservedObject var storeSubscriber: StoreSubscriber<AppState> init() { storeSubscriber = StoreSubscriber(store: store) } var body: some View { VStack { Image(storeSubscriber.state.currentUser.avatarURL) .resizable() .frame(width: 100, height: 100) Text(storeSubscriber.state.currentUser.username) } } }
Dispatch Actions
To update state, dispatch actions to the store—just like in Redux:
// Update avatar Button("Update Avatar") { store.dispatch(AppAction.updateAvatar(url: "new_avatar.png")) } // Toggle article like Button(action: { store.dispatch(AppAction.toggleArticleLike(articleId: article.id)) }) { Image(systemName: article.isLiked ? "heart.fill" : "heart") .foregroundColor(article.isLiked ? .red : .gray) }
- Combine + ObservableObject: Best for native, dependency-free solutions—great if you want to use Apple's built-in tools.
- ReSwift: Perfect if you want to replicate Redux's exact workflow (actions, reducers, single source of truth) in Swift.
Both approaches will solve your syncing problems smoothly, just like Redux does in React Native.
内容的提问来源于stack exchange,提问作者K.Wu

