我需要帮助在 SwiftUI 应用程序中构建数据。我的结构中有一个变异函数,但“if let”改变了副本而不是原始的

问题描述 投票:0回答:1

我正在构建一个 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”,但这会导致可选数据出现更多问题。

ios swift swiftui concurrency
1个回答
0
投票

做事的方式有些复杂,但各有各的。

您可以尝试这种方法,在您的

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")
     }
 }
© www.soinside.com 2019 - 2024. All rights reserved.