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的正确数据流向应该是:
- ViewModel持有业务状态:所有需要处理的输入数据、业务状态都放在ViewModel中,用
@Published实现响应式更新。 - 视图绑定ViewModel:视图通过
@EnvironmentObject或@ObservedObject获取ViewModel,双向绑定其状态属性,用户输入直接更新ViewModel的值。 - ViewModel处理业务逻辑:视图触发操作时,调用ViewModel的方法,ViewModel用自身持有的状态数据完成业务处理(比如调用Firebase接口)。
内容的提问来源于stack exchange,提问作者Kiarra J.
相关产品推荐
相关产品推荐

