如何使用 Combine 框架和 SwiftUI 发布网络请求的数据

Posted

技术标签:

【中文标题】如何使用 Combine 框架和 SwiftUI 发布网络请求的数据【英文标题】:how to publish the data of a network request using Combine framework and SwiftUI 【发布时间】:2019-10-30 12:05:05 【问题描述】:

我正在使用 swiftUI 和 Combine 框架以及 MVVM 开发一个 ios 应用程序。 我想在一个名为 LoginService 的单独类中处理登录 API 请求,该类在 LoginViewModel 中使用。 现在我想知道我应该如何发布和观察视图和视图模型之间的属性。 我的意思是 ViewModel 是一个 ObservableObject 并且正在 View 中被观察,但是由于我在 Service 类中处理网络请求,LoginService 应该如何通知 LoginViewModel 和 LoginView 接收到数据并且应该更新 View?

import Foundation
import Combine

class LoginViewModel: ObservableObject 

    @Published var user = UserModel()

    @Published var LoginStatus: Bool = false

    @Published var LoginMessage: String = ""

    var service = LoginService()

    func Login(With email: String, And password: String) -> Bool 
        service.validateLogin(email: email, password: password)
        return false
    


这是 LoginViewModel 的代码。 当从服务器接收到数据通知视图时,LoginService 应该如何更改 LoginStatus、LoginMessage 和 user 的值? 我这么说是因为据我所知,您只能在 View(SwiftUI) 中观察 ObservableObjects。

【问题讨论】:

【参考方案1】:

好的,我以你的为例,我会做以下事情:

我假设您的服务返回 truefalse

import Foundation
import Combine

class LoginViewModel: ObservableObject 

    @Published var LoginStatus: Bool = false

    @Published var LoginMessage: String = ""

    var service = LoginService()

    func Login(_ email: String, _ password: String) -> Bool 
        self.LoginStatus  = service.validateLogin(email: email, password: password)
        return self.LoginStatus
    


在你看来:


import SwiftUI

struct ContentView : View 

    @ObservedObject var model = LoginViewModel()

    var body: some View 
        VStack 
            Button(action: 
                _ = self.model.Login("TestUser", "TestPassword")
            , label: 
                Text("Login")
            )
            Text(self.model.LoginStatus ? "Logged In" : "Not Logged in")
        
    


应该是这样的。

我删除了UserModel,因为你不应该嵌套模型。

我希望这会有所帮助。

编辑 1:

要自动验证某些内容,您可以在视图上使用 onApear() 或者使用olnrecieve() 监听更改以更新 UI 或状态

import SwiftUI

struct ContentView : View 

    @ObservedObject var model = LoginViewModel()

    var body: some View 
        VStack 
            Button(action: 
                _ = self.model.Login("TestUser", "TestPassword")
            , label: 
                Text("Login")
            )
            Text(self.model.LoginStatus ? "Logged In" : "Not Logged in")
        .onAppear 
            // call a function that gets something from your server 
            // and modifies your state
            self.model.validate()
        .onReceive(self.model.$LoginMessage, perform:  message in
               // here you can update your state or your ui
               // according the LoginMessage... this gets called
               // whenever LoginMessage changes in your model
        )
    


【讨论】:

感谢您的回复,但我的问题仍未得到解答。现在我从服务类中的 API 获取数据,我应该如何在视图中访问它们?假设我的 API 返回一个令牌和一个 ID,我想在 View 中显示 ID。 当您更改模型中的@Published 变量时,它会自动更新所有使用它们的视图...我展示了在模型中更改的视图中使用布尔值。通常在处理网络时,您需要在 DispatchQueue.main.async 块中更新 @Published 变量 要自动触发验证,您可以使用onAppear()我将编辑答案【参考方案2】:

您已建立单向绑定,即;模型 -> 视图

现在,无论您的服务如何更新模型,无论何时模型更改,查看更新都无关紧要。

您的服务可以通过多种方式更新模型。

    常用URLSession,在回调中照常更新LoginStatus和LoginMessage。

    通过联合发布者,例如; URLSessionDataTaskPublisher,并在其接收器闭包中进行更新。

【讨论】:

以上是关于如何使用 Combine 框架和 SwiftUI 发布网络请求的数据的主要内容,如果未能解决你的问题,请参考以下文章

使用 Combine 和 SwiftUI 在 Realm 中观察收集结果

SwiftUI + Combine:如何将数据分配给带有动画的模型

使用 SwiftUI、Combine 和 Firebase,我如何在将用户的帐户链接到电子邮件/密码之前验证用户是不是已匿名登录?

SwiftUI ObjectBinding 不会使用 combine 接收来自可绑定对象的 didchange 更新

如何使用 Combine 的 CurrentValueSubject 并在 SwiftUI 视图中访问它?

使用 SwiftUI/Combine,如何避免在 ViewModel 中放置可取消项