MVVM架构下@Observable类引用的状态管理困惑咨询
SwiftUI MVVM中@Observable引用外部状态的解决方案
问题背景
在SwiftUI的MVVM架构中,当@Observable类引用外部对象状态时,容易出现状态变化无法被正确观察的问题。比如开发需要位置授权的应用时,单例LocationManager维护授权状态,但ViewModel中的关联属性无法同步更新:
初始实现代码
LocationManager:
final class LocationManager: NSObject { static let shared = LocationManager() private let locationManager = CLLocationManager() var isAuthorized = false override init() { super.init() locationManager.delegate = self } } extension LocationManager: CLLocationManagerDelegate { func locationManagerDidChangeAuthorization(_ lm: CLLocationManager) { switch locationManager.authorizationStatus { case .authorizedAlways, .authorizedWhenInUse: isAuthorized = true default: isAuthorized = false } } }
ContentView及ViewModel:
struct ContentView: View { @State var viewModel = ViewModel() var body: some View { SomeOtherView() .sheet(isPresented: $viewModel.isNotAuthorized) { Text("Please authorize location services") .interactiveDismissDisabled() } } @Observable class ViewModel { var isNotAuthorized = !LocationManager.shared.isAuthorized } }
上述代码中,viewModel.isNotAuthorized仅在初始化时赋值,后续LocationManager.isAuthorized变化无法触发视图更新。尝试的几种方案均有顾虑:
- 将
LocationManager标记为@Observable,不确定是否符合编码规范; - 视图直接依赖
LocationManager,会导致状态管理混乱; - 把状态推到各ViewModel用回调实现,模型无法持有统一状态,不切实际。
推荐解决方案:让LocationManager支持@Observable
直接将LocationManager改为@Observable类,使其状态变化能被SwiftUI自动追踪,同时严格遵循MVVM职责划分:
修改后的LocationManager
@Observable final class LocationManager: NSObject { static let shared = LocationManager() private let locationManager = CLLocationManager() var isAuthorized = false override init() { super.init() locationManager.delegate = self // 初始化时同步当前授权状态 updateAuthorizationStatus() } private func updateAuthorizationStatus() { switch locationManager.authorizationStatus { case .authorizedAlways, .authorizedWhenInUse: isAuthorized = true default: isAuthorized = false } } } extension LocationManager: CLLocationManagerDelegate { func locationManagerDidChangeAuthorization(_ lm: CLLocationManager) { updateAuthorizationStatus() } }
优化后的ViewModel与视图
struct ContentView: View { @State var viewModel = ViewModel() var body: some View { SomeOtherView() .sheet(isPresented: $viewModel.isNotAuthorized) { Text("请授权位置服务") .interactiveDismissDisabled() .onTapGesture { viewModel.requestAuthorization() } } } @Observable class ViewModel { private let locationManager = LocationManager.shared // 计算属性自动同步外部状态 var isNotAuthorized: Bool { !locationManager.isAuthorized } // 业务逻辑封装在ViewModel中 func requestAuthorization() { locationManager.locationManager.requestWhenInUseAuthorization() } } }
方案优势
- 单一可信源:所有授权状态由
LocationManager统一维护,避免多副本不一致; - 清晰职责划分:
LocationManager负责状态维护,ViewModel负责状态转换与业务逻辑,视图仅做UI渲染; - 自动状态追踪:借助
@Observable特性,无需手动订阅或回调,SwiftUI自动处理依赖更新。
兼容Combine的替代方案
如果项目仍使用Combine,可以通过PassthroughSubject实现状态发布与订阅:
修改后的LocationManager
import Combine final class LocationManager: NSObject { static let shared = LocationManager() private let locationManager = CLLocationManager() var isAuthorized = false { didSet { authorizationSubject.send(isAuthorized) } } let authorizationSubject = PassthroughSubject<Bool, Never>() override init() { super.init() locationManager.delegate = self updateAuthorizationStatus() } private func updateAuthorizationStatus() { switch locationManager.authorizationStatus { case .authorizedAlways, .authorizedWhenInUse: isAuthorized = true default: isAuthorized = false } } } extension LocationManager: CLLocationManagerDelegate { func locationManagerDidChangeAuthorization(_ lm: CLLocationManager) { updateAuthorizationStatus() } }
ViewModel订阅状态
@Observable class ViewModel { private let locationManager = LocationManager.shared private var cancellables = Set<AnyCancellable>() var isNotAuthorized = false init() { // 订阅状态变化并更新自身属性 locationManager.authorizationSubject .map { !$0 } .assign(to: \.isNotAuthorized, on: self) .store(in: &cancellables) // 初始化同步初始状态 isNotAuthorized = !locationManager.isAuthorized } func requestAuthorization() { locationManager.locationManager.requestWhenInUseAuthorization() } }
内容的提问来源于stack exchange,提问作者bli00
相关产品推荐
相关产品推荐

