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

Swift中View数据无法传递至ViewModel的Firebase认证问题排查

问题诊断与修复:Firebase认证密码长度错误 + MVVM数据传递问题

你的核心问题是AuthViewModel错误地直接创建MainView实例获取输入值,这完全违背了MVVM的设计逻辑,也是触发密码长度错误的根本原因:

每次调用authModel.register()或authModel.login()时,你都会新建一个MainView()实例——这个新实例和用户正在交互的MainView毫无关联,它的email和password都是初始空字符串""。所以Firebase收到的密码是空值,自然触发"Password must be 6 characters or more."的验证错误。

以下是分步修复方案:


1. 修正AuthViewModel:将输入状态移到ViewModel中

MVVM架构中,视图的输入状态应该由ViewModel管理,而非视图自身持有。修改后的代码如下:

import Foundation
import Firebase

class AuthViewModel: ObservableObject {
    // 用@Published包装状态,实现视图与ViewModel的双向绑定
    @Published var email = ""
    @Published var password = ""
    
    func register() {
        // 直接使用ViewModel自身持有的状态数据
        Auth.auth().createUser(withEmail: email, password: password) { result, error in
            if let error = error {
                print(error.localizedDescription)
                return
            }
            // 注册成功后的后续逻辑(比如跳转页面)
            print("注册成功")
        }
    }
    
    func login() {
        Auth.auth().signIn(withEmail: email, password: password) { result, error in
            if let error = error {
                print(error.localizedDescription)
                return
            }
            // 登录成功后的后续逻辑
            print("登录成功")
        }
    }
}

2. 修正MainView:绑定ViewModel的状态

移除MainView自身的@State属性,改为直接绑定环境中的ViewModel状态:

import SwiftUI
import Firebase

struct MainView: View {
    // 通过环境对象获取ViewModel实例
    @EnvironmentObject var authModel: AuthViewModel
    
    var body: some View {
        ZStack {
            Color.theme.blue
            
            RoundedRectangle(cornerRadius: 30, style: .continuous)
                .foregroundStyle(LinearGradient(colors: [.orange, .red], startPoint: .topLeading, endPoint: .bottomTrailing))
                .frame(width: 1000, height: 450)
                .rotationEffect(.degrees(15))
                .offset(x: 20)
            
            VStack(spacing: 20) {
                
                Image("logo")
                    .resizable()
                    .scaledToFill()
                    .frame(width: 100)
                    .offset(y: 10)
                
                Text("Welcome")
                    .foregroundColor(.white)
                    .font(Font.custom("Poppins-Bold", size: 40))
                    .offset(y:-10)
                
                // 绑定ViewModel的email属性
                TextField("", text: $authModel.email)
                    .foregroundColor(.white)
                    .textFieldStyle(.plain)
                    .placeholder(when: authModel.email.isEmpty) {
                        Text("Email Address")
                            .foregroundColor(.white)
                            .font(Font.custom("Poppins-Light", size: 20))
                    }
                
                Rectangle()
                    .frame(width:350, height: 1)
                    .foregroundColor(.white)
                    .padding(.top, -5)
                
                // 绑定ViewModel的password属性
                SecureField("", text: $authModel.password)
                    .foregroundColor(.white)
                    .textFieldStyle(.plain)
                    .placeholder(when: authModel.password.isEmpty) {
                        Text("Password")
                            .foregroundColor(.white)
                            .font(Font.custom("Poppins-Light", size: 20))
                    }
                
                Rectangle()
                    .frame(width:350, height: 1)
                    .foregroundColor(.white)
                    .padding(.top, -5)
                
                Button {
                    authModel.register()
                } label: {
                    Text("Sign Up")
                        .frame(width: 200, height: 40)
                        .background(.orange)
                        .cornerRadius(10)
                        .foregroundColor(.black)
                        .font(Font.custom("Poppins-Medium", size: 18))
                }
                
                Spacer()
                Spacer()
                
                HStack {
                    Text("Already Have An Account?")
                        .foregroundColor(.white)
                        .font(Font.custom("Poppins-Medium", size: 18))
                    Button {
                        authModel.login()
                    } label: {
                        Text("Login")
                            .foregroundColor(.orange)
                            .font(Font.custom("Poppins-Medium", size: 18))
                    }
                }.offset(y: 100)
            }
            .frame(width: 350, height: 60)
        }.ignoresSafeArea()
    }
}

struct MainView_Previews: PreviewProvider {
    static var previews: some View {
        MainView().environmentObject(AuthViewModel())
    }
}

extension View {
    func placeholder<Content: View>(
        when shouldShow: Bool,
        alignment: Alignment = .leading,
        @ViewBuilder placeholder: () -> Content) -> some View {

        ZStack(alignment: alignment) {
            placeholder().opacity(shouldShow ? 1 : 0)
            self
        }
    }
}

3. 保持SplashScreenView的环境对象注入不变

你在SplashScreenView中注入AuthViewModel的方式是正确的,无需修改:

if isActive {
    withAnimation{
        MainView().environmentObject(AuthViewModel())
    }
}

补充说明

你之前尝试的@Published var mainView = MainView()同样错误,因为这只是创建了一个独立的MainView实例,和用户界面上正在交互的实例没有任何关联,无法获取真实的输入数据。

MVVM的正确数据流向应该是:

  1. ViewModel持有业务状态:所有需要处理的输入数据、业务状态都放在ViewModel中,用@Published实现响应式更新。
  2. 视图绑定ViewModel:视图通过@EnvironmentObject或@ObservedObject获取ViewModel,双向绑定其状态属性,用户输入直接更新ViewModel的值。
  3. ViewModel处理业务逻辑:视图触发操作时,调用ViewModel的方法,ViewModel用自身持有的状态数据完成业务处理(比如调用Firebase接口)。

内容的提问来源于stack exchange,提问作者Kiarra J.

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 16:45:32