我想使用@Observable而不是@ObservableObject重构以下代码。
import Foundation
import FirebaseAuth
enum AuthenticationState {
case unauthenticated
case authenticating
case authenticated
}
enum AuthenticationFlow {
case login
case signUp
}
@MainActor
class AuthenticationViewModel: ObservableObject {
@Published var email = ""
@Published var password = ""
@Published var confirmPassword = ""
@Published var flow: AuthenticationFlow = .login
@Published var isValid = false
@Published var authenticationState: AuthenticationState = .unauthenticated
@Published var errorMessage = ""
@Published var user: User?
@Published var displayName = ""
init() {
registerAuthStateHandler()
$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)
}
private var authStateHandler: AuthStateDidChangeListenerHandle?
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 = ""
}
}
我在苹果关于“从 Observable 对象协议迁移到 Observable 宏”的文档的帮助下重构如下。
import Foundation
import FirebaseAuth
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 = ""
init() {
registerAuthStateHandler()
$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)
}
private var authStateHandler: AuthStateDidChangeListenerHandle?
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 = ""
}
}
最具挑战性的部分是
$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)
重构此代码的正确方法是什么?
Xcode 抱怨它无法在范围内找到 $flow。
通常你会从 ObservableObject 重构为 .task 例如
.task(id: loginConfig) {
user = await loginController.login(loginInfo)
}
其中 loginConfig 是一个包含字段的结构,控制器是一个带有异步函数的环境结构,因此我可以模拟它以进行预览。