Skip to content

Instantly share code, notes, and snippets.

@SwiftyAlex
Last active September 15, 2019 12:53
Show Gist options
  • Save SwiftyAlex/2a195af55fa903b5d533f22d9ca324f6 to your computer and use it in GitHub Desktop.
Save SwiftyAlex/2a195af55fa903b5d533f22d9ca324f6 to your computer and use it in GitHub Desktop.
A Simple, TypeSafe routing protocol built around Combine.
public protocol Route {
/// Request & ResponseType are forced, but `EmptyRequest` allows you to skip encoding/decoding
associatedtype RequestType: Encodable
associatedtype ResponseType: Decodable
static var method: HTTPMethod { get }
static var path: String { get }
/// Optional instance of `RequestType` that gets encoded if present
var requestObject: RequestType? { get set }
/// Any query parameters.
var queryParemeters: [URLQueryItem] { get set }
/// This function has a generic implenetation for the more simple routes, all others should have their own.
func toRequest() -> URLRequest
}
extension Route {
/// The basic implementation of `Route` `toRequest` adds parameters and builds the path.
public func toRequest() -> URLRequest {
// Add all the parameters
var urlComponents = URLComponents(string: "https://jsonplaceholder.typicode.com/" + Self.path)!
urlComponents.queryItems = queryParemeters
var request = URLRequest(url: urlComponents.url!)
// If we're using a route that has to send data, lets encode it here.
// In the case any special mapping needs to be done, this method should be overriden.
if let requestObject = requestObject, !(requestObject is EmptyRequest) {
request.httpBody = try? JSONEncoder().encode(requestObject)
}
return request
}
}
/// A Router using `Combine`
public static func performRequest<R: Route>(_ route: R, data: R.RequestType) -> AnyPublisher<R.ResponseType, Error> {
return URLSession.shared.dataTaskPublisher(for: route.toRequest())
.map(\.data)
.mapError { NetworkError(from: $0) }
.decode(type: R.ResponseType.self, decoder: JSONDecoder())
.mapError { _ in NetworkError.decodingFailed }
.eraseToAnyPublisher()
}
// Return a publisher and then do a thing
public static func performRequest<R: Route>(_ route: R) -> AnyPublisher<URLResponse, Error> where R.ResponseType == EmptyRequest {
return URLSession.shared.dataTaskPublisher(for: route.toRequest())
.map(\.response)
.mapError { NetworkError(from: $0) }
.eraseToAnyPublisher()
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment