What is the difference between marking a variable with/without @Published in a class conforming observableObject?

huangapple go评论62阅读模式
英文:

What is the difference between marking a variable with/without @Published in a class conforming observableObject?

问题

大家好,

我目前正在开发一个SwiftUI项目,遇到了一个关于在视图模型中使用@Published的问题。我在视图模型中有一个名为'errorMessage'的变量。

我注意到,即使我从变量中移除了@Published,应用程序仍然可以正常运行。我已经使用@EnvironmentObject将视图模型注入到SwiftUI环境中,但我并没有在视图模型中明确标记变量为@Published,也没有使用objectWillChange.send()。

我的问题是:在这种情况下,当需要观察属性并触发视图更新时,我是否仍然应该明确使用@Published?在这种情况下,使用@Published和不使用它之间可能有什么区别?是否有任何最佳实践或潜在影响我应该知道的?

我将非常感谢与此主题相关的任何见解、建议或示例。提前感谢您的帮助!

import SwiftUI

struct LoginView: View {
    @EnvironmentObject var authService: AuthService
    @Environment(\.dismiss) private var dismiss

    var body: some View {
        ZStack {
            Text(authService.errorMessage)
        }
        .onSubmit {
            Task {
                if await authService.signIn(){
                     dismiss()
                }
            }
        }
    }
}

import Foundation
import FirebaseAuth

enum AuthError: Error {
    case emptyPassword
    case emptyEmail
}

extension AuthError: LocalizedError {
    var errorDescription: String? {
        switch self {
        case .emptyPassword:
            return NSLocalizedString("请提供密码以继续。", comment: "")
        case .emptyEmail:
            return NSLocalizedString("请提供电子邮件地址以继续。", comment: "")
        }
    }
}

@MainActor
final class AuthService: ObservableObject {
    @Published var email = ""
    @Published var password = ""
    
    private(set) var errorMessage = ""

    private func validation() throws {
        if email.isEmpty {
            throw AuthError.emptyEmail
        }
        if password.isEmpty {
            throw AuthError.emptyPassword
        }
    }
    
    func signIn() async -> Bool {
        do {
            try validation()
            let authResult = try await auth.signIn(withEmail: email, password: password)
            return true
        } catch {
            errorMessage = error.localizedDescription
            return false
        }
    }
}

我已经查阅了Apple提供的相关文档,包括Published、EnvironmentObject、ObservableObject等重要概念。

英文:

everyone,

I'm currently working on a SwiftUI project and have encountered a question regarding the usage of @Published in a view model. I have a login view with a variable 'errorMessage' in the view model.

I noticed that even when I removed @Published from the variable, the app still functions correctly. I have injected the view model into the SwiftUI environment using @EnvironmentObject, but I haven't explicitly marked the variable with @Published or used objectWillChange.send() in the view model.

My question is: Under this scenario, should I still use @Published explicitly when a property needs to be observed and trigger view updates? What could be the difference between declaring a variable with @Published and without it in this context? Are there any best practices or potential implications that I should be aware of?

I would greatly appreciate any insights, suggestions, or examples related to this topic. Thank you in advance for your help!

import SwiftUI

struct LoginView: View {
    @EnvironmentObject var authService: AuthService
    @Environment(\.dismiss) private var dismiss

    var body: some View {
        ZStack {
            Text(authService.errorMessage)
        }
        .onSubmit {
            Task {
                if await authService.signIn(){
                     dismiss()
                }
            }
        }
    }
}

import Foundation
import FirebaseAuth

enum AuthError: Error {
    case emptyPassword
    case emptyEmail
}

extension AuthError: LocalizedError {
    var errorDescription: String? {
        switch self {
        case .emptyPassword:
            return NSLocalizedString("Please enter a password to continue.", comment: "")
        case .emptyEmail:
            return NSLocalizedString("Please enter an email address to continue.", comment: "")
}

@MainActor
final class AuthService: ObservableObject {
    @Published var email = ""
    @Published var password = ""
    
    private(set) var errorMessage = ""

    private func validation() throws {
        if email.isEmpty {
            throw AuthError.emptyEmail
        }
        if password.isEmpty {
            throw AuthError.emptyPassword
        }
    }
    
    func signIn() async -> Bool {
        do {
            try validation()
            let authResult = try await auth.signIn(withEmail: email, password: password)
            return true
        } catch {
            errorMessage = error.localizedDescription
            return false
        }
    }
}

I have examined the pertinent concepts (Published, EnvironmentObject, ObservableObject etc.) within the documentation provided by Apple.

答案1

得分: 1

以下是要翻译的内容:

  1. 使用View结构和属性包装器,而不是传统的视图模型对象,这可以提高性能并防止一致性错误。在View结构中使用let是最简单的特性:当let的值与上次初始化View时的值不同时,会调用body。使用Combine框架中的@Published自行重新实现这个基本模式是低效的。
  2. 在使用async/await时,使用.task.task(id: signIn),而不是Combine的ObservableObject,这可以提供取消和重启功能。
  3. 使用自定义的EnvironmentKey来提供服务,就像AuthorizationController的实现方式一样。
英文:

Here are a few best practices:

  1. Use the View struct and property wrappers instead of a legacy view model object, this increases performance and prevents consistency bugs. let in a View struct is the simplest feature: body is called when the let value changes from the last time the View was init. It's inefficient to re-implement this basic pattern yourself with @Published in ObservableObject from the Combine framework.
  2. Use .task and .task(id: signIn) instead of Combine's ObservableObject when using async/await, this gives you cancellation and restart features.
  3. Use a custom EnvironmentKey for a service, like how AuthorizationController is implemented.

huangapple
  • 本文由 发表于 2023年6月1日 14:02:58
  • 转载请务必保留本文链接:https://go.coder-hub.com/76379070.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定