Combine framework tutorial - Part 3 - Error handling with mapError, setFailureType, & flatMap
tryMap
사용: 데이터 매핑 시 에러 발생 시 throw
mapError
사용: 특정 에러를 다른 데이터 타입으로 매핑func fetch(url: URL) -> AnyPublisher<Data, APIError> {
return URLSession
.shared
.dataTaskPublisher(for: url)
.tryMap { (data, response) -> Data in
if let response = response as? HTTPURLResponse,
!(200...299).contains(response.statusCode) {
throw APIResources.APIError.badResponse(statusCode: response.statusCode)
} else {
return data
}
}
.mapError { error in
APIResources.APIError.convert(error: error)
}
.eraseToAnyPublisher()
}
tryMap
을 통해 에러를 throw 또는 실제 데이터를 스트리밍mapError
를 통해 특정한 에러를 커스텀 에러 타입으로 캐스팅 가능enum APIError: Error, CustomStringConvertible {
case url(URLError?)
case badResponse(statusCode: Int)
case unknown(Error)
static func convert(error: Error) -> APIError {
switch error {
case is URLError: return .url(error as? URLError)
case is APIError: return error as! APIError
default: return .unknown(error)
}
}
var description: String {
return ""
}
}
private func addSubscriber() {
guard let url = URL(string: urlString) else { return }
URLSession
.shared
.dataTaskPublisher(for: url)
.receive(on: DispatchQueue.global(qos: .background))
.tryMap { output in
return output.data
}
.decode(type: [AlbumModel].self, decoder: JSONDecoder())
.sink { completion in
switch completion {
case .finished: print("Successfully get album info")
case .failure(let error): print(error.localizedDescription)
}
} receiveValue: { [weak self] returnedValue in
guard let self = self else { return }
self.albumSubject.send(returnedValue)
}
.store(in: &cancellables)
imageUrlSubject
.removeDuplicates()
.compactMap { url in
return URL(string: url)
}
.setFailureType(to: APIResources.APIError.self)
.flatMap(maxPublishers: .max(4)) { url -> AnyPublisher<UIImage, APIResources.APIError> in
self.apiResources
.fetch(url: url)
.compactMap({UIImage(data: $0)})
.catch { _ in
Empty()
}
.eraseToAnyPublisher()
}
.scan([UIImage]()) { all, next in
return all + [next]
}
.receive(on: DispatchQueue.main)
.sink(receiveCompletion: { completion in
}, receiveValue: { [weak self] images in
self?.imagesSubject.send(images)
})
.store(in: &cancellables)
}
imageUrlSubject
를 구독할 때 실패 형식을 APIError
로 정의, 이후 flatMap
메소드에서 커스텀 타입의 퍼블리셔를 리턴