在我的以 MVVM 模式设计的应用程序中,我有一个登录视图。如果存在网络或身份验证问题,登录可能会失败。我的目标是捕获错误并显示相应的警报。我为警报定义了枚举,如下所示:
enum Alerts: Identifiable {
var id: Int {
return self.hashValue
}
case networkError
case authenticationError
}
该视图的实现是:
struct LoginView: View {
@ObservedObject var viewModel = LoginViewModel()
var body: some View {
VStack {
TextField("Enter e-mail address", text: $viewModel.email)
SecureField("Enter password", text: $viewModel.password)
Button("Log In") {
viewModel.login()
}
}.alert(item: $viewModel.errorAlert, content: { alert in
switch alert {
case .networkError:
return Alert(title: Text("Error"), message: Text("Check internet Connection"), dismissButton: .default(Text("Ok")))
case .authenticationError:
return Alert(title: Text("Error"), message: Text("Some error occured, please try again"), dismissButton: .default(Text("Ok")))
}
})
}
}
视图模型是:
class LoginViewModel: ObservableObject {
@Published var email: String = ""
@Published var password: String = ""
@Published var errorAlert: Alerts? = nil
@Published var token: Token? = nil
var authentication = PassthroughSubject<User, WebserviceError>()
var cancellables = Set<AnyCancellable>()
init() {
authentication.map { Webservice().authenticate($0) }.switchToLatest().print().sink { error in
self.errorAlert = Alerts.networkError
} receiveValue: { token in
self.token = token
}.store(in: &cancellables)
token.map { KeychainWrapper.save(token: $0)}?.sink(receiveCompletion: { error in
self.errorAlert = Alerts.authenticationError
}, receiveValue: { _ in
//
}).store(in: &cancellables)
}
func login() {
authentication.send(User(username: email, password: password))
}
}
Web服务的实现
class Webservice {
func authenticate(_ user: User) -> AnyPublisher<Token, WebserviceError> {
return Future<Token, WebserviceError> { promis in
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
if user.username.lowercased() == "root" && user.password == "1234" {
promis(.success(Token(access: "asdasda", refresh: "sdfsdfsdf", exprationDate: Date().addingTimeInterval(120))))
} else {
promis(.failure(.unknown))
}
}
}.eraseToAnyPublisher()
}
}
考虑以下场景:
用户第一次运行该应用程序。在第一次尝试中,她/他输入了错误的用户名/密码。
发生的情况是,用户将看到等效的警报,并且可以单击“确定”将其关闭。
问题是,从第二次开始,单击登录按钮后,没有任何反应。看起来 Viewmodel 中的authentication.map { .... } 被永远取消了。为什么会这样?