将@ObservableObject类重构为@Observable时,绑定流代码报错的解决方法
问题:@Observable宏重构后Combine绑定流报错“无法在作用域中找到$flow”
将基于@ObservableObject的AuthenticationViewModel类重构为使用@Observable宏后,初始化方法中使用$flow、$email等绑定的Combine流处理代码出现Xcode报错“无法在作用域中找到$flow”,需要调整该段代码的实现方式。
原核心报错代码(基于@ObservableObject)
$flow .combineLatest($email, $password, $confirmPassword) .map { flow, email, password, confirmPassword in flow == .login ? !(email.isEmpty || password.isEmpty) : !(email.isEmpty || password.isEmpty || confirmPassword.isEmpty) } .assign(to: &$isValid)
解决方案
@Observable宏不会像@Published那样自动生成$前缀的Combine发布器属性,需要通过publisher(for: KeyPath)方法手动获取每个属性的发布流,同时调整订阅的存储方式:
修改步骤:
- 在ViewModel类中添加
Combine框架导入(如果未导入),并新增一个存储订阅的集合:
import Combine // ... private var cancellables = Set<AnyCancellable>()
- 将原代码中的
$属性名替换为self.publisher(for: \.属性名),并修改assign的调用方式,将订阅存入cancellables:
修改后的初始化方法代码
init() { registerAuthStateHandler() let flowPublisher = self.publisher(for: \.flow) let emailPublisher = self.publisher(for: \.email) let passwordPublisher = self.publisher(for: \.password) let confirmPasswordPublisher = self.publisher(for: \.confirmPassword) flowPublisher .combineLatest(emailPublisher, passwordPublisher, confirmPasswordPublisher) .map { flow, email, password, confirmPassword in flow == .login ? !(email.isEmpty || password.isEmpty) : !(email.isEmpty || password.isEmpty || confirmPassword.isEmpty) } .assign(to: \.isValid, on: self) .store(in: &cancellables) }
完整重构后的ViewModel代码
import Foundation import FirebaseAuth import Combine enum AuthenticationState { case unauthenticated case authenticating case authenticated } enum AuthenticationFlow { case login case signUp } @MainActor @Observable class AuthenticationViewModel { var email = "" var password = "" var confirmPassword = "" var flow: AuthenticationFlow = .login var isValid = false var authenticationState: AuthenticationState = .unauthenticated var errorMessage = "" var user: User? var displayName = "" private var cancellables = Set<AnyCancellable>() private var authStateHandler: AuthStateDidChangeListenerHandle? init() { registerAuthStateHandler() let flowPublisher = self.publisher(for: \.flow) let emailPublisher = self.publisher(for: \.email) let passwordPublisher = self.publisher(for: \.password) let confirmPasswordPublisher = self.publisher(for: \.confirmPassword) flowPublisher .combineLatest(emailPublisher, passwordPublisher, confirmPasswordPublisher) .map { flow, email, password, confirmPassword in flow == .login ? !(email.isEmpty || password.isEmpty) : !(email.isEmpty || password.isEmpty || confirmPassword.isEmpty) } .assign(to: \.isValid, on: self) .store(in: &cancellables) } func registerAuthStateHandler() { if authStateHandler == nil { authStateHandler = Auth.auth().addStateDidChangeListener { auth, user in self.user = user self.authenticationState = user == nil ? .unauthenticated : .authenticated self.displayName = user?.displayName ?? user?.email ?? "" } } } func switchFlow() { flow = flow == .login ? .signUp : .login errorMessage = "" } private func wait() async { do { print("Wait") try await Task.sleep(nanoseconds: 1_000_000_000) print("Done") } catch { print(error.localizedDescription) } } func reset() { flow = .login email = "" password = "" confirmPassword = "" } }
内容的提问来源于stack exchange,提问作者LeEuler
相关产品推荐
相关产品推荐

