处理 Combine 中的错误 (Swift, iOS)
Handling errors in Combine (Swift, iOS)
我不知道如何处理合并流程中的错误。我希望能够从 Combine 函数中捕获错误。
任何人都可以帮助解释我在这里做错了什么以及我应该如何处理 捕获 Combine 的错误?
Note: The function below is just an example to illustrate a case where an error could be caught instead of crashing the app.
func dataFromURL<T: Decodable>(_ url: String, _ decoder: JSONDecoder = JSONDecoder()) -> AnyPublisher<T, Error> {
// 1) Example: If the URL is not well-formatted, I would like to /create/raise/return an error (in a Combine way)
// 2) Instead of the forced unwrapping here, I would also prefer to raise a catchable error if the creation of the request fails
let request = URLRequest(url: URL(string:url)!)
// 3) Any kind of example dealing with potential errors, etc
return urlSession
.dataTaskPublisher(for: request)
.tryMap { result -> T in
return try decoder.decode(T.self, from: result.data)
}
.receive(on: DispatchQueue.main)
.eraseToAnyPublisher()
}
// function in another file:
func result() {
// I would like to be able to catch or handle errors in this function
dataFromURL("test").print()
// Example : if error 1), else if error 2) etc
}
如评论中所述,我希望能够捕获 dataFromURL
函数之外但在 "Combine way" 函数中的任何错误。
我以 URL 数据获取为例,但它可以与其他任何东西一起使用。
使用 Combine 流程引发和捕获错误的推荐方法是什么?例如,是 return 有特定错误的发布者吗?如果可以,我该怎么做?
编辑
如果没有 Combine,我只会抛出一个错误,将 throws
关键字添加到函数中,然后在 result
函数中捕获错误。
但我希望 Combine 有更简单或更优雅的方式来实现这一点。例如,也许可以随时抛出的东西:
guard <url is valid> else {
return PublisherError(URLError.urlNotValid)
}
并且可能被这样抓到:
dataFromURL
.print()
.onError { error in
// handle error here
}
.sink { result in
// no error
}
如果 URL(string:)
初始化器失败(返回 nil
),你必须决定你想把它变成什么错误。假设您想将其变成 URLError
。因此,如果 URL(string:)
returns nil
,创建 URLError
并使用 Fail
发布者发布它:
func jsonContents<T: Decodable>(
ofUrl urlString: String,
as type: T.Type,
decodedBy decoder: JSONDecoder = JSONDecoder()
) -> AnyPublisher<T, Error> {
guard let url = URL(string: urlString) else {
let error = URLError(.badURL, userInfo: [NSURLErrorKey: urlString])
return Fail(error: error).eraseToAnyPublisher()
}
return URLSession.shared
.dataTaskPublisher(for: url)
.tryMap { result -> T in
return try decoder.decode(T.self, from: result.data)
}
.receive(on: DispatchQueue.main)
.eraseToAnyPublisher()
}
但是如果你真的想把更多的 Combine 铲进去,你可以使用 Result.Publisher
而不是 Fail
:
func jsonContents<T: Decodable>(
ofUrl urlString: String,
as type: T.Type,
decodedBy decoder: JSONDecoder = JSONDecoder()
) -> AnyPublisher<T, Error> {
return (
URL(string: urlString)
.map { Result.success([=11=]) } // This is Optional.map
?? Result.failure(URLError(.badURL, userInfo: [NSURLErrorKey: urlString]))
)
.publisher
.flatMap({
URLSession.shared
.dataTaskPublisher(for: [=11=])
.tryMap { result -> T in
return try decoder.decode(T.self, from: result.data)
}
})
.receive(on: DispatchQueue.main)
.eraseToAnyPublisher()
}
但是事情变得难以阅读。我们可以将 Result
的使用分解为一个新的运算符,unwrapOrFail(with:)
:
extension Publisher {
func unwrapOrFail<Wrapped>(with error: Failure) -> Publishers.FlatMap<Result<Wrapped, Self.Failure>.Publisher, Self> where Output == Wrapped? {
return self
.flatMap ({
[=12=]
.map { Result.success([=12=]).publisher }
?? Result.failure(error).publisher
})
}
}
然后像这样使用它:
func jsonContents<T: Decodable>(
ofUrl urlString: String,
as type: T.Type,
decodedBy decoder: JSONDecoder = JSONDecoder()
) -> AnyPublisher<T, Error> {
return Result.success(urlString).publisher
.map { URL(string: [=13=]) }
.unwrapOrFail(with: URLError(.badURL, userInfo: [NSURLErrorKey: urlString]))
.flatMap({
URLSession.shared
.dataTaskPublisher(for: [=13=])
.tryMap { result -> T in
return try decoder.decode(T.self, from: result.data)
}
})
.receive(on: DispatchQueue.main)
.eraseToAnyPublisher()
}
但是请注意,如果您在此过程中犯了任何错误,您可能会收到难以理解的错误消息,并且必须拆开您的长管道才能让 Swift 告诉您真正的问题所在。
我不知道如何处理合并流程中的错误。我希望能够从 Combine 函数中捕获错误。
任何人都可以帮助解释我在这里做错了什么以及我应该如何处理 捕获 Combine 的错误?
Note: The function below is just an example to illustrate a case where an error could be caught instead of crashing the app.
func dataFromURL<T: Decodable>(_ url: String, _ decoder: JSONDecoder = JSONDecoder()) -> AnyPublisher<T, Error> {
// 1) Example: If the URL is not well-formatted, I would like to /create/raise/return an error (in a Combine way)
// 2) Instead of the forced unwrapping here, I would also prefer to raise a catchable error if the creation of the request fails
let request = URLRequest(url: URL(string:url)!)
// 3) Any kind of example dealing with potential errors, etc
return urlSession
.dataTaskPublisher(for: request)
.tryMap { result -> T in
return try decoder.decode(T.self, from: result.data)
}
.receive(on: DispatchQueue.main)
.eraseToAnyPublisher()
}
// function in another file:
func result() {
// I would like to be able to catch or handle errors in this function
dataFromURL("test").print()
// Example : if error 1), else if error 2) etc
}
如评论中所述,我希望能够捕获 dataFromURL
函数之外但在 "Combine way" 函数中的任何错误。
我以 URL 数据获取为例,但它可以与其他任何东西一起使用。
使用 Combine 流程引发和捕获错误的推荐方法是什么?例如,是 return 有特定错误的发布者吗?如果可以,我该怎么做?
编辑
如果没有 Combine,我只会抛出一个错误,将 throws
关键字添加到函数中,然后在 result
函数中捕获错误。
但我希望 Combine 有更简单或更优雅的方式来实现这一点。例如,也许可以随时抛出的东西:
guard <url is valid> else {
return PublisherError(URLError.urlNotValid)
}
并且可能被这样抓到:
dataFromURL
.print()
.onError { error in
// handle error here
}
.sink { result in
// no error
}
如果 URL(string:)
初始化器失败(返回 nil
),你必须决定你想把它变成什么错误。假设您想将其变成 URLError
。因此,如果 URL(string:)
returns nil
,创建 URLError
并使用 Fail
发布者发布它:
func jsonContents<T: Decodable>(
ofUrl urlString: String,
as type: T.Type,
decodedBy decoder: JSONDecoder = JSONDecoder()
) -> AnyPublisher<T, Error> {
guard let url = URL(string: urlString) else {
let error = URLError(.badURL, userInfo: [NSURLErrorKey: urlString])
return Fail(error: error).eraseToAnyPublisher()
}
return URLSession.shared
.dataTaskPublisher(for: url)
.tryMap { result -> T in
return try decoder.decode(T.self, from: result.data)
}
.receive(on: DispatchQueue.main)
.eraseToAnyPublisher()
}
但是如果你真的想把更多的 Combine 铲进去,你可以使用 Result.Publisher
而不是 Fail
:
func jsonContents<T: Decodable>(
ofUrl urlString: String,
as type: T.Type,
decodedBy decoder: JSONDecoder = JSONDecoder()
) -> AnyPublisher<T, Error> {
return (
URL(string: urlString)
.map { Result.success([=11=]) } // This is Optional.map
?? Result.failure(URLError(.badURL, userInfo: [NSURLErrorKey: urlString]))
)
.publisher
.flatMap({
URLSession.shared
.dataTaskPublisher(for: [=11=])
.tryMap { result -> T in
return try decoder.decode(T.self, from: result.data)
}
})
.receive(on: DispatchQueue.main)
.eraseToAnyPublisher()
}
但是事情变得难以阅读。我们可以将 Result
的使用分解为一个新的运算符,unwrapOrFail(with:)
:
extension Publisher {
func unwrapOrFail<Wrapped>(with error: Failure) -> Publishers.FlatMap<Result<Wrapped, Self.Failure>.Publisher, Self> where Output == Wrapped? {
return self
.flatMap ({
[=12=]
.map { Result.success([=12=]).publisher }
?? Result.failure(error).publisher
})
}
}
然后像这样使用它:
func jsonContents<T: Decodable>(
ofUrl urlString: String,
as type: T.Type,
decodedBy decoder: JSONDecoder = JSONDecoder()
) -> AnyPublisher<T, Error> {
return Result.success(urlString).publisher
.map { URL(string: [=13=]) }
.unwrapOrFail(with: URLError(.badURL, userInfo: [NSURLErrorKey: urlString]))
.flatMap({
URLSession.shared
.dataTaskPublisher(for: [=13=])
.tryMap { result -> T in
return try decoder.decode(T.self, from: result.data)
}
})
.receive(on: DispatchQueue.main)
.eraseToAnyPublisher()
}
但是请注意,如果您在此过程中犯了任何错误,您可能会收到难以理解的错误消息,并且必须拆开您的长管道才能让 Swift 告诉您真正的问题所在。