我正在构建一个 SwiftUI 应用程序,我的一个模型有一个变异函数,其目的是在发生更改时帮助更新 UI。例如,如果用户更新他们的用户名,我只想调用变异函数来更新模型并返回 UI。
但是,由于我从服务器获取这些数据,因此它有可能为零。因此,通过使用“if let”展开,它会创建一个副本,并且变异函数不再更新 UI。这是我的设置以获取更多上下文。
配置文件视图模型 这是我的个人资料视图模型。我有一个状态枚举,可以帮助我根据状态在 UI 端进行渲染。
@Observable class ProfileViewModel {
enum State {
case idle
case loading
case failed
case success
}
private(set) var state: State = .idle
private(set) var userProfile: UserProfile? = nil
private let manager: UserManager
init(manager: UserManager) {
self.manager = manager
}
func getUserProfile() async {
guard case .idle = state else {
return
}
state = .loading
do {
let profile = try await manager.getUserProfile()
state = .success
userProfile = profile
} catch {
state = .failed
}
}
func signOut() async -> () {
do {
try await manager.signOut()
} catch {
print(error)
}
}
}
个人资料视图 这就是我的问题真正开始的地方。首先,如果我们有一个 .success 案例,这意味着我们得到了数据。但我仍然需要打开它并创建一个副本。当我调用 userProfile.updateUsername() 时,它会更新副本的用户名,并且不会导致 UI 上重新渲染。
我可以做什么来解决这个问题或更好地处理我的数据?我最终也想将模型传递给子视图。
struct ProfileView: View {
@State private var profileViewModel = ProfileViewModel(
manager: UserManager()
)
var body: some View {
VStack {
switch profileViewModel.state {
case .success:
if var userProfile = profileViewModel.userProfile {
Text("@\(userProfile.username)")
.fontDesign(.monospaced)
Button {
userProfile.updateUsername()
} label: {
Text("update")
}
}
case .loading:
ProgressView()
case .failed:
Text("Sorry, something went wrong.")
default:
EmptyView()
}
}
.task {
await profileViewModel.getUserProfile()
}
}
}
用户资料
struct UserProfile: Decodable {
private(set) var id: String
private(set) var username: String
private(set) var fullName: String?
private(set) var avatarURL: String?
private(set) var about: String?
private(set) var website: String?
private(set) var joined: String
enum CodingKeys: String, CodingKey {
case id
case username
case fullName = "full_name"
case avatarURL = "avatar_url"
case about
case website
case joined
}
mutating func updateUsername() {
username = "justUpdateAndRerenderTheUIPlease"
}
}
我尝试过使用“if let”,并且只是检查“profileViewModel!= nil”,但这会导致可选数据出现更多问题。
做事的方式有些复杂,但各有各的。
您可以尝试这种方法,在您的
ProfileViewModel
添加
func updateUsername() {
userProfile?.updateUsername()
}
因为你有
private(set) var userProfile: UserProfile? = nil
。
并且在
ProfileView
有
if var userProfile = profileViewModel.userProfile {
Text("@\(userProfile.username)").fontDesign(.monospaced)
Button {
profileViewModel.updateUsername() // <--- here
} label: {
Text("update")
}
}