Best strategies to handle errors in Combine

0 0 0

Người đăng: Phạm Trung Huy

Theo Viblo Asia

Handling errors properly is essential for creating a robust and reliable applications. Error handling in reactive programming is comparatively more complex than its imperative counterpart. But when using Combine, you’re equipped with handy operators that can help us handle the errors properly.

This article assume you have the basic knowledge about Combine includes Publisher, Subscriber,…. You can also check my series about Combine at here

Errors types in Combine

Before we learn about handling errors strategies, it’s crucial to understands different types of errors that can occur when you using Combine

  • Publisher errors: These errors occur when a publisher fails to produce a value due to an internal error, such as a network failure or a runtime error.
  • Operator errors: These errors occur when an operator in the pipeline fails to process a value due to an error condition, such as an invalid argument or a runtime error.
  • Subscription errors: These errors occur when a subscriber fails to receive values due to an error condition, such as a cancelled subscription or a runtime error.

mapError

mapError operator is used for mapping an error to the expected error type

import Combine enum MyError: Error { case testError
} enum MappedError: Error { case transformedError
} let publisher = PassthroughSubject<Int, MyError>() let cancellable = publisher .mapError { _ in MappedError.transformedError } .sink(receiveCompletion: { completion in switch completion { case .finished: print("Publisher completed successfully.") case .failure(let error): print("Publisher completed with error: \(error)") } }, receiveValue: { value in print("Received value: \(value)") }) publisher.send(1)
publisher.send(completion: .failure(.testError)) //Outputs
//Received value: 1
//Publisher completed with error: transformedError

retry

Another common strategy for handling errors in Combine is to use the retry operator. You might want to use the retry operator before actually accepting an error when working with data requests.

enum MyError: Error { case unknown
} let url = URL(string: "https://example.comm")! let cancellable = URLSession.shared.dataTaskPublisher(for: url) .mapError { error -> Error in if let urlError = error as? URLError, urlError.code == .networkConnectionLost { return urlError } else { return MyError.unknown } } .retry(3) .sink(receiveCompletion: { completion in switch completion { case .finished: print("Request completed successfully.") case .failure(let error): print("Request failed with error: \(error)") } }, receiveValue: { value in print("Received value: \(value)") })

However, The retry operator in Combine does not have the same functionality as the retry(when:) operator in RxSwift. In Combine, the retry operator simply resubscribes to the upstream publisher when an error occurs, up to a specified number of times. It does not provide a mechanism to conditionally decide whether to retry based on the error that occurred.

catch

One of the most common strategies for handling errors in Combine is to use the catch operator. The catch operator allows you to handle errors and recover from failures in a reactive pipeline.

let publisher = URLSession.shared.dataTaskPublisher(for: url) .map(\.data) .catch { error -> Just<Data> in print("Error: \(error)") return Just(Data()) // return a default value if an error occurs }

For example, you have a publisher that retrieves data from server, you can use catch operator to catch any errors and recover by returning a default value or retrying the request

replaceError

replaceError is seem quite the same to catch operator. The difference is replaceError completely ignores the error and still return a recovering value.

In the above example, we’re doing nothing than return the placeholder image in case of error.

URLSession.shared .dataTaskPublisher(for: URL(string: "https://mydomain/image_654")!) .map { result -> UIImage in return UIImage(data: result.data) ?? UIImage(named: "placeholder-image")! } .replaceError(with: UIImage(named: "placeholder-image")!) .sink(receiveCompletion: { print("received completion: \($0)") }, receiveValue: {print("received auth: \($0)")})

Conclusion

Recognizing the importance of handling both happy and unhappy scenarios, it's vital to discuss error management strategies in Combine. Also, you can check out the code snippet featured in this article via my playground 🙌

Bình luận

Bài viết tương tự

- vừa được xem lúc

Quản lý self và cancellable trong Combine.

. . Công việc quản lý bộ nhớ memory management thường trở nên phức tạp khi chúng ta thực hiện các tác vụ bất đồng bộ asynchronous vì chúng ta thường phải lưu giữ một số object nằm ngoài scope mà object được define trong khi vẫn phải đảm bảo được việc giải phóng object đó được thực hiện đúng quy trìn

0 0 43

- vừa được xem lúc

Đơn giản hoá mô hình MVVM + SwiftUI

1. Tổng quan.

0 0 22

- vừa được xem lúc

Combine trong Swift: Mở Ra Thế Giới Mới của Lập Trình Reactive

Swift là một ngôn ngữ lập trình mạnh mẽ, được Apple thiết kế để phát triển ứng dụng cho các hệ điều hành của mình. Điều đó có nghĩa là nó không chỉ cung cấp các tính năng cơ bản của một ngôn ngữ lập t

0 0 26

- vừa được xem lúc

RxSwift và Combine, chọn gì trong thế giới lập trình Reactive?

Lập trình Reactive đã trở thành một phong cách lập trình phổ biến trong cộng đồng phát triển phần mềm hiện đại. Trong môi trường phát triển ứng dụng iOS, RxSwift và Combine là hai thư viện quan trọng,

0 0 20

- vừa được xem lúc

Introduction to Combine in iOS

What is Combine. . Key concept. Publisher.

0 0 13

- vừa được xem lúc

Publisher in Combine: Unveiling the Publisher Role

Continue with the Combine series, today we will discuss Publisher. Combine needs something that can model a data stream.

0 0 15