【问题标题】:Swift Combine - How to throw an error and stop executionSwift Combine - 如何抛出错误并停止执行
【发布时间】:2020-12-10 04:00:49
【问题描述】:

我有一个 HTTP 请求发布者,当返回 401 错误时,我想停止执行并显示我的登录屏幕。

这是我的部分代码:

cancellable = fetcher.hello(helloRequest: HelloRequest(name: self.name))
            .print("fetcher.hello")
            .catch { _ in
                // TODO: how to handle errors with request?
                Just(HelloResponse.placeHolder)
            }
            .flatMap { response -> AnyPublisher<HelloResponse, Never> in
                if response.imageUrl == nil || response.imageUrl == "" {
                    // If there's no image to download just return the response
                    return Just(response).eraseToAnyPublisher()
                }
                else {
                    // Chain together request and download image
                    return fetcher.downloadImage(url: response.imageUrl!)
                        .print("fetcher.hello.downloadImage")
                        .catch { _ in
                            // If there was an error downloading the image, replace it with a placeholder
                            Just(UIImage(named: "placeholder_square")!)
                        }
                        .map {
                            // Add image to response
                            HelloResponse(message: response.message, visitCount: response.visitCount, imageUrl: response.imageUrl, image: $0)
                        }
                        .eraseToAnyPublisher()
                }
            }
            .sink(receiveCompletion: { _ in }, receiveValue: { self.response = $0.self })

所以问题的一部分是下面的 flatMap 会在必要时下载图像。输出类型是 AnyPublisher (我想不出另一种方法)。现在,catch 返回一个占位符模型并且工作正常。但现在我已经接受了这个错误。我认为 Empty() 发布者可能会工作,但它似乎不正确。我试过 Fail() 但显然 catch 是一个 Never (有道理)。谢谢!

【问题讨论】:

  • tryCatch 将允许您抛出错误。我仍然很困惑...fetcher.hello 可以发送失败吗?你不必catch它(它会吞下它)——这不是你想要的吗?
  • 是的 fetch.hello 返回 AnyPublisher。我会看看tryCatch。我想删除 Just(HelloResponse.placeHolder)。实际上对于 401 以外的错误,我正在考虑添加一个 retry(),但我现在不在那里。基本上我想停止 401 错误(不要尝试拉图像),并显示我的登录屏幕。谢谢。
  • 401 检查在哪里发生?这发生在fetch.hello 中吗?在检查发生的地方,您可以在那里返回错误。这将导致下游.flatMap 被跳过
  • 所以改成AnyPublisher&lt;HelloResponse, Error&gt;,用.setFailureType(to:)匹配内部发布者,比如Just
  • @DennisCalla 请不要在问题的编辑中包含答案。答案是答案。要么是新开发者,要么你应该给它作为答案。然后您应该接受该答案,从而结束问答循环。否则,这将永远悬而未决。谢谢。

标签: swift combine


【解决方案1】:

使用 New Dev 关于 setFailureType 的建议,我现在可以正常工作了。当我最初让 flatMap 返回 AnyPublisher 时,它不会编译,因为 Just 是 Never。所以我将 flatMap 更改为 Never fail 但后来我无法删除 catch,因为之前的失败类型是 Error。将 setFailureType 添加到 flatMap 中的 Justs 现在让我将 flatMap 更改为返回 Error,然后我删除了 catch。

cancellable = fetcher.hello(helloRequest: HelloRequest(name: self.name))
            .print("fetcher.hello")
            .flatMap { response -> AnyPublisher<HelloResponse, Error> in
                if response.imageUrl == nil || response.imageUrl == "" {
                    // If there's no image to download just return the response
                    return Just(response)
                        .setFailureType(to: Error.self) // This allows us to set a failure type (Just is Never) so that it will match AnyPublisher<HelloResponse, Error>
                        .eraseToAnyPublisher()
                }
                else {
                    // Chain together request and download image
                    return fetcher.downloadImage(url: response.imageUrl!)
                        .print("fetcher.hello.downloadImage")
                        .catch { _ in
                            // If there was an error downloading the image, replace it with a placeholder
                            Just(UIImage(named: "placeholder_square")!)
                                .setFailureType(to: Error.self) // This allows us to set a failure type (Just is Never) so that it will match AnyPublisher<HelloResponse, Error>
                        }
                        .map {
                            // Add image to response
                            HelloResponse(message: response.message, visitCount: response.visitCount, imageUrl: response.imageUrl, image: $0)
                        }
                        .eraseToAnyPublisher()
                }
            }
            .sink(receiveCompletion: { completion in
                switch completion {
                case .finished:
                    break
                case .failure(let error):
                    print("Request error: \(String(describing: error))")
                }
            }, receiveValue: {
                self.response = $0.self
            })
    }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-02-03
    • 2021-09-20
    • 1970-01-01
    • 2017-06-20
    • 2012-03-19
    • 1970-01-01
    • 2021-09-29
    • 1970-01-01
    相关资源
    最近更新 更多