receiveCompletion 出错时订阅取消

2024-02-11

在我的以 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 { .... } 被永远取消了。为什么会这样?


根据文档 https://developer.apple.com/documentation/combine/receiving-and-handling-events-with-combine

发布者继续发出元素,直到正常完成或失败。

这意味着如果您的发布商(authentication: PassthroughSubject) 失败,它不能再向其订阅者发出值。

解决这个问题的一种方法可能是调用Webservice().authenticate($0)手动并使用它直接返回的发布者(并完全删除身份验证主题)。

注意:我不是合并专家,我不确定这是否是管理发布者的正确方法(如果频繁存储可取消项会出现某种性能问题),您可能需要深入研究。

class LoginViewModel: ObservableObject {

    // [...]

    func login() {
        Webservice()
            .authenticate(User(username: email, password: password))
            .print()
            .sink { error in
                self.errorAlert = Alerts.networkError
            } receiveValue: { token in
                self.token = token
            }
            .store(in: &cancellables)
    }
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

receiveCompletion 出错时订阅取消 的相关文章

  • Swift iOS 9.2 中的每日本地通知

    尝试快速发送每日本地通知 然而 由于某种原因 它只是每分钟发送一次 我希望第一个通知在应用程序打开后 30 分钟发送 然后每天重复此通知 在 swift fie 中我有以下代码 每日通知代码 同时在应用程序委托中添加部分 让 Date NS
  • iOS 中通过 USB 进行反向端口转发

    我在桌面上有一个 Web 套接字服务器 在 iPhone 设备上有一个客户端 我想使用 USB 而不是任何网络与他们通信 我已经使用 adb reverse 在 android 上实现了它 但无法找到适用于 iOS 的任何解决方案 我尝试使
  • 如何在 macOS 上的 SwiftUI 中设置拖动图像

    我正在尝试更改 GridView 的默认拖放预览图像 该图像似乎包含网格中的所有可见项目 据我了解 我应该能够设置NSItemProvider previewImageHandler块来自定义所使用的图像 我似乎找不到任何关于返回自定义图像
  • 锁定时 AVAudioPlayer 在 iPhone 5 中不播放音频

    使用 AVAudioPlayer 我尝试在 iphone 播放器播放时播放声音 当设备锁定时也是如此 问题是 在 iPhone 4s ios 7 中 效果很好 但在带有 6 和 7 ios 的 iPhone 5 上没有任何反应 In the
  • iOS - 基于设备的不同图像或缩放相同的图像?

    似乎开发人员总是为不同的设备创建不同的图像资源 并根据设备加载它们 但是 只为最高分辨率的设备 iPad 创建图像 然后为 iPhone 6 5 等缩小该图像 有什么缺点吗 我使用 SpriteKit 因此我只需创建不同大小的 SKSpri
  • 如何在UIWindow中添加视图?

    我想添加一个视图UIWindow与以下代码 AppDelegate delegate AppDelegate UIApplication sharedApplication delegate UIWindow window delegate
  • Swift 每 5 天重复一次 LocalNotification

    如何每 5 天上午 10 00 重复一次 LocalNotification 我尝试这个 但它不起作用 let content UNMutableNotificationContent content title Hello content
  • 自定义“可搜索”搜索字段 SwiftUI iOS 15

    When using the new searchable modifier in SwiftUI on iOS 15 I have no way to customize the Search Bar appearance Specifi
  • iOS图片上的线测量

    我需要一些帮助来开始绘制末端带有圆圈的线条 并测量其长度 我能够划清界限 但无法让它移动 花了几个小时决定在 SO 上发布 因此 请参阅下图并指导我开始 任何使用 Objective C 的示例或教程都会有帮助 谢谢 这个想法看起来实现起来
  • iOS 应用程序崩溃 com.apple.root.background-qos

    在应用程序中发现应用程序崩溃 我怀疑这可能是由于 firebase 观察者的代码而发生的 由于在用户案例中 用户可以从一个事件转到用户配置文件 参与此事件 然后从用户配置文件可以返回到此事件 我需要一个 ref 句柄来删除特定的观察者 因此
  • 如何在 Swift 中将 Int 转换为字符

    我在这里挣扎了十多分钟 失败了 我屈服了 我需要在 Swift 中将 Int 转换为 Character 但无法解决它 Question 你如何转换 cast an Int integer to a Character char 在斯威夫特
  • UITextField 中光标闪烁,但键盘不出现

    我得到了一个带有文本字段的简单详细视图 在详细的viewController中我写了这段代码 void viewDidAppear BOOL animated self textField becomeFirstResponder NSLo
  • 电话号码的 Swift 2 国家/地区代码

    我正在尝试从 iPhone 用户处获取国家 地区代码 我不确定是否有有关该国家 地区以及某些应用程序 如 Viber 如何建议拨号代码的信息 我到目前为止 let currentLocale NSLocale currentLocale l
  • 在 ios 中从 XMPP 服务器检索存档的消息

    我正在将 XMPP 功能集成到我的 ios 应用程序中 但遇到了一个无法解决的问题 问题是我无法从服务器获取存档的消息 我的客户能够登录 并且我已经成功测试了多个服务调用 发送 接收消息 获取有关用户的信息 发送后
  • 自调整大小的集合视图在 iOS 15 中进入递归循环

    我有一个自动调整大小的集合视图 当我调用 super layoutSubviews 时 我的应用程序崩溃 因为集合视图进入递归更新循环 这在 iOS 14 及更低版本中运行良好 但从 iOS 15 开始就观察到了这一点 class Dyna
  • 如何使用CAAnimation制作曲线/圆弧动画?

    我有一个用户界面 其中一个项目被删除 我想模仿 iOS 邮件中的 移动到文件夹 效果 小字母图标被 扔 到文件夹中的效果 我的会被扔进垃圾箱 我尝试使用它来实现它CAAnimation在图层上 据我在文档中阅读 我应该能够设置byValue
  • 当设备方向改变时以编程方式更新约束的正确方法?

    我在用着storyboard and autolayout 并将 IB 中的约束设置为IBOutlet在相应的视图控制器中 我正在阅读几篇关于如何将纵向和横向的约束更新为不同的帖子 但我仍然不确定应该如何执行此操作 我应该设置新的限制吗 v
  • 在 try 中使用零合并运算符? for 抛出并返回可选值的函数

    我想在以下两种情况下使用 nil coalescing 运算符设置默认值 函数抛出错误 函数返回 nil 请看一下下面的代码片段 我有以下问题 为什么 item1 为零 item1和item2的初始化有什么区别 enum VendingMa
  • 如何在 iOS 中通过 wifi 传输大文件

    我下载了WiTap http developer apple com library ios samplecode WiTap Introduction Intro html代码来自苹果网站 它用于通过本地 WiFi 网络传输数据 我正在从
  • 允许在 Safari 上聊天应用程序使用 audio.play()

    由于苹果禁用了自动播放音频的功能HTMLMedia Element play https developer mozilla org en US docs Web API HTMLMediaElement play在没有用户交互的 java

随机推荐

  • 从领域数据库中获取单个列 (Android)

    我是领域的初学者 我有一个包含 3 列的表 其名称为ID 姓名 电子邮件 地址 为了获取 Name 列的数据 我们使用如下查询 从表名中选择名称 对于 SQLite 如果我们在Android中使用Realm 那么我们必须使用哪种方法来只获取
  • jinja2 如何删除尾随换行符

    我正在使用 jinja 2 输出 yaml 文件 但似乎无法摆脱尾随换行符和 for 循环的末尾 例如下面的 request path path headers origin somedomain com user agent agent
  • 解雇ViewController:不工作

    我有一个名为 vc0 的视图控制器 其显示如下 self presentViewController vc1 animated YES completion nil 在 vc1 中 我有一个按钮来呈现另一个视图控制器 self presen
  • 获取 Java 数组部分的视图[重复]

    这个问题在这里已经有答案了 我指的是在Java中只获取数组的一部分 https stackoverflow com questions 11001720 get only part of an array in java 它使用范围复制 方
  • set_time_limit 不起作用

    我有一个bigintphp 中的类 用于计算大数 除了时间限制外 效果很好 我设置了时间限制 set time limit 900 在我的 bigint php 文件中 它在本地主机中工作 但在我的网络主机中 当我尝试计算 999 999
  • Angular 7:如何将文件/图像与我的反应式表单一起提交?

    我创建了带有文本输入的简单反应式表单 当提交表单时 我想传递文件输入中的图像 每次我用谷歌搜索时 我都会得到教程 它们向我展示如何上传文件 但它是在没有其他输入字段的情况下完成的 我知道如何做到这一点 但我不明白如何在一次提交中提交表单和文
  • 带 MonoTouch 的 sizeWithFont

    MonoTouch 中的 sizeWithFont 相当于什么 如果没有这样的方法 是否可以通过其他方式调用它 我想做的就是根据文本调整标签大小 下一个更新 MonoTouch 1 1 将提供 UIView StringSize strin
  • NSURLErrorDomain错误代码1002描述

    我是新来的iOS发展 我正在尝试加载一个JSON 这是我的功能 func loadmyJSON urlPath String let url NSURL NSURL string urlPath let session NSURLSessi
  • 从 SQLite 中的 DATETIME 获取月份

    我正在尝试从 a 中提取月份DATETIMESQLite 中的字段 month dateField 效果不太好strftime m dateStart 有任何想法吗 我不明白 答案就在你的问题中 select strftime m date
  • 如何通过 Websocket 发送文件以及附加信息?

    我正在开发一个 Web 应用程序 用于从管理界面将图像 视频等发送到两个显示器 我在服务器端使用 Node js 中的 ws 我已经实现了选择服务器上可用的图像和外部 URL 并将它们发送到客户端 但我也希望能够通过文件输入直接发送从设备中
  • 单选按钮单击和重新单击

    我在 Android 中有一个单选按钮组 选择项目时我会收到事件 目前为止还正常 但如果用户单击已选择的项目 我不会收到该事件 有没有办法知道 接收事件 用户何时点击单选按钮 无论它是否被选择 多谢 我不明白为什么当单击已选中的单选按钮时会
  • c++11 std::hash 函数对象类线程安全

    在 c 11 中声明的哈希函数类对象
  • 使用共享存储库模型的 GitHub 协作

    有人可以向我提供 GitHub 协作备忘单 供希望对存储库具有平等访问 权利的两人团队使用吗 我对使用分叉的必要性感到困惑 这对于开发人员分散的大型开源项目来说似乎是有意义的 但当我和我的搭档彼此相距 10 英尺时 似乎有点矫枉过正 Tha
  • Git:如何在同一分支中的提交之上重新建立分支? [复制]

    这个问题在这里已经有答案了 参考文献这个优秀的答案 https stackoverflow com a 38430972 3398271 我读过并且 我认为 我理解了 但据我所知并没有回答我的具体问题 因为它解释了如何重新基于提交中的提交
  • Selenium Firefox IDE 可以实现吗?

    这是这个问题的后续 想要创建一个表单填充程序 java jsp html 足够吗 https stackoverflow com questions 17329996 want to create a form filler is java
  • Dropbox Saver 可以接受来自 createObjectURL() 的数据吗?

    Dropbox Chooser 和 Saver 工具 https www dropbox com developers dropins对于这两项任务来说似乎非常酷 让用户从 Dropbox 中选择一个文件 页面就可以下载它 即 Dropbo
  • 块与 Objective-C 中的普通方法和函数有何不同?

    与 Objective C 中的普通方法和函数相比 使用块有什么优势 我已阅读文档 但找不到块而不是其他语言功能的具体用法 我确信我错过了一些东西 所以有人可以用比现有文档更简单的方式解释块的优点吗 块是一种包装一段代码并有效存储它以供以后
  • 将 Angularjs 和 Rails 应用程序分离为独立组件

    我想尝试一下 Angularjs 然而 我一直难以决定应该将我的角度应用程序放在哪里 我使用 Rails 框架作为后端 我看过教程 其中整个角度应用程序都位于 asset javascript 文件夹下 我想知道是否可以让它完全位于我的ra
  • Tailwind CSS:有没有办法定位下一个兄弟?

    我有一个带有如下标签的无线电输入 输入被隐藏 标签用于制作一个视觉上吸引人的圆圈供用户单击
  • receiveCompletion 出错时订阅取消

    在我的以 MVVM 模式设计的应用程序中 我有一个登录视图 如果存在网络或身份验证问题 登录可能会失败 我的目标是捕获错误并显示相应的警报 我为警报定义了枚举 如下所示 enum Alerts Identifiable var id Int