Skip to content

Instantly share code, notes, and snippets.

What would you like to do?
An experiment of how to use Combine+URLRequest.
import Foundation
import Combine
import PlaygroundSupport
func request<Failure: Error, Output: Publisher>(url: URLRequest,
session: URLSession = .shared,
queue: DispatchQueue = .main,
parser: @escaping (Data) -> Output,
parseError: @escaping (Error) -> Failure,
completion: @escaping (Result<Output.Output, Failure>) -> Void) -> AnyCancellable {
return session.dataTaskPublisher(for: url)
.tryMap({ (output) -> Data in
if let httpResponse = output.response as? HTTPURLResponse, !(200..<300).contains(httpResponse.statusCode) {
throw URLError(URLError.Code(rawValue: httpResponse.statusCode))
} else {
.flatMap({ parser($0).mapError(parseError) })
.receive(on: queue)
.sink(receiveCompletion: { (result) in
if case .failure(let error) = result {
}, receiveValue: { (response) in
/// This extension allows a Rx Data parser
extension String {
enum StringParseError: Error {
case invalidString
static func parsePublisher(from data: Data) -> AnyPublisher<String, StringParseError> {
return String.parsePublisher(from: data, encoding: .utf8)
static func parsePublisher(from data: Data, encoding: Encoding) -> AnyPublisher<String, StringParseError> {
if let output = String(data: data, encoding: encoding) {
return Just(output)
.mapError({ _ in StringParseError.invalidString }) //This mapError is here only to have a single return type
} else {
return Fail(error: StringParseError.invalidString)
let url = URLRequest(url: URL(string: "")!)
let cancellable = request(url: url,
parser: String.parsePublisher(from:),
parseError: { $0 },
completion: { (result: Result<String, Error>) in
PlaygroundPage.current.needsIndefiniteExecution = true
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.