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

将@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)方法手动获取每个属性的发布流,同时调整订阅的存储方式:

修改步骤:

  1. 在ViewModel类中添加Combine框架导入(如果未导入),并新增一个存储订阅的集合:
import Combine
// ...
private var cancellables = Set<AnyCancellable>()
  1. 将原代码中的$属性名替换为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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 12:35:04