可取消项目的数量越来越高,这正常吗



我有一个SwiftUI登录视图,其中有两个文本字段用于用户名、密码和一个登录按钮。我使用的是MVVM设计模式,希望我的viewModel在用户单击登录按钮后进行身份验证。

该视图的实现如下

struct LoginView: View {

@ObservedObject var viewModel = LoginViewModel()

var body: some View {

VStack(alignment: .leading) {

TextField("Enter e-mail address", text: $viewModel.email)
TextField("Enter password", text: $viewModel.password)

Button("Login") {
viewModel.login()
}
}
}
}

和viewModel

class LoginViewModel: ObservableObject {

@Published var email: String = ""
@Published var password: String = ""

var authenticate = PassthroughSubject<User, Never>()
var cancellables = Set<AnyCancellable>()

init() {
authenticate.sink { user in
Webservice().authenticate(username: user.username, passsword: user.password).sink { error in
//
} receiveValue: { token in
//
}.store(in: &self.cancellables)
}.store(in: &cancellables)
}

func login() {
authenticate.send(User(username: email, password: password))
}
}

一切都很好,但每当用户单击登录按钮时,可取消项的数量就会增加。我想这不应该是那样的。这里怎么了?

订阅的数量不应该随着每个发出的值而增加。在您的情况下,它确实会增加的原因是因为sink中有一个sink——内部sink可取消项会添加到每个authenticate.send()发射值的集合中。

但是这种嵌套的sink并不是解决这个问题的正确方法。相反,您应该通过将发出User值的authenticate与创建一个新发布者的东西链接来创建一个管道,该发布者实际上完成了";变换";User(通过进行登录web请求(到令牌。

您通常可以使用flatMap进行这种链接,但在您的情况下,您希望取消之前的请求,因此您应该使用mapswitchToLatest:的组合

authenticate
.map { user in 
Webservice().authenticate(username: user.username, password: user.password)
}
.switchToLatest()
.sink(receiveCompletion: { completion in
//
}, receiveValue: { token in 
//
})
.store(in: &cancellables)

这是一个从authenticate.send开始的单个订阅,并在最后获得一个令牌作为值。

每次点击登录:

  1. 您在authenticate中发送了一个新的用户事件
  2. authenticate的信宿中,创建一个全新的请求并将其存储到可取消项中

第二点是可取消项正在增加的地方,在获得响应后可能已经是completed,但将随着可取消项的释放而清除。这很正常。

但可能不正常的是,当您请求新的登录尝试时,最好取消之前的登录尝试(如果尚未完成(。如果您想这样做,请将可从Web服务中取消的实例分开,并在尝试新实例之前取消前一个实例。

private var loginCancellable: AnyCancellable?
// ... in init
authenticate.sink { user in
loginCancellable.cancell()
loginCancellable = Webservice().authenticate(username: user.username, passsword: user.password).sink { error in
//
} receiveValue: { token in
//
}
// ... rest of the code    

注意:在我的脑海中写下所有这些代码。未经测试。你可能需要稍微调整一下。但希望你能明白。

更新:新开发人员的答案是您应该追求的。RxSwift中的FlatMapLatest本质上表示为Combinemap { .. }.switchToLatest()。总的来说,当以被动的方式工作时,总是为链接解决方案而不是关闭而蓬勃发展。我上面的方法可以给你一个想法,你在概念上错过了什么。

最新更新