Skip to content

Instantly share code, notes, and snippets.

@antonyalkmim
Last active November 24, 2022 21:42
Show Gist options
  • Select an option

  • Save antonyalkmim/3a021a0011e5225a14aaf4c80fff7802 to your computer and use it in GitHub Desktop.

Select an option

Save antonyalkmim/3a021a0011e5225a14aaf4c80fff7802 to your computer and use it in GitHub Desktop.
A URLSession approach to organize Network layer
import Foundation
import RxSwift
extension HttpService: ReactiveCompatible { }
extension Reactive where Base: HttpServiceType {
func request(_ endpoint: Base.Target) -> Single<Data> {
return Single<Data>.create(subscribe: { [weak base] single in
let task = base?.request(endpoint, responseData: { result in
switch result {
case Result.success(let data):
single(SingleEvent.success(data))
case Result.failure(let error):
single(SingleEvent.error(error))
}
})
return Disposables.create { task?.cancel() }
})
}
}
extension PrimitiveSequence where TraitType == SingleTrait, ElementType == Data {
/// Maps received data at key path into a Decodable object. If the conversion fails, the signal errors.
public func map<D: Decodable>(_ type: D.Type, using decoder: JSONDecoder = JSONDecoder()) -> Single<D> {
return flatMap { data in
do {
let res = try decoder.decode(type, from: data)
return Single<D>.just(res)
} catch {
return Single<D>.error(HttpError.jsonMapping(error))
}
}
}
}
//
// HttpService.swift
// antonyalkmim
//
// Created by Antony Alkmim on 15/05/18.
// Copyright © 2018 Antony Alkmim. All rights reserved.
//
import Foundation
protocol TargetType {
var baseURL: URL { get }
var path: String { get }
var method: HttpMethod { get }
var jsonParameters: [String: Any] { get }
var headers: [String: String]? { get }
}
extension TargetType {
func urlRequest() -> URLRequest {
// generate url
let urlPath = [self.baseURL.absoluteString, self.path].joined()
let url = URL(string: urlPath)!
var request = URLRequest(url: url)
/// http method
request.httpMethod = self.method.rawValue
/// body
switch self.method {
case .get: break
default:
request.httpBody = try? JSONSerialization.data(withJSONObject: self.jsonParameters, options: [])
}
/// headers
self.headers?.forEach { it in
request.addValue(it.value, forHTTPHeaderField: it.key)
}
return request
}
}
enum Result {
case failure(Error)
case success(Data)
}
enum HttpMethod: String {
case get = "GET"
case post = "POST"
case put = "PUT"
case delete = "DELETE"
}
enum HttpError: Swift.Error {
case jsonMapping(Error?)
case invalidJson
}
protocol HttpServiceType: class {
associatedtype Target: TargetType
func request(_ endpoint: Target, responseData: @escaping (Result) -> Void) -> URLSessionDataTask
}
class HttpService<Target: TargetType>: HttpServiceType {
private var requestClosure: (Target) -> URLRequest
private var session: URLSession
// MARK: - Initializer
init(
configuration: URLSessionConfiguration = URLSessionConfiguration.default,
requestClosure: @escaping ((Target) -> URLRequest) = { $0.urlRequest() }
) {
self.session = URLSession(configuration: configuration)
self.requestClosure = requestClosure
}
func request(_ endpoint: Target, responseData: @escaping (Result) -> Void) -> URLSessionDataTask {
//1 - pass through interceptors
let request = requestClosure(endpoint)
//2 - execute task
let task = URLSession.shared.dataTask(with: request) { data, _, error in
guard let data = data else {
responseData(Result.failure(error!))
return
}
responseData(Result.success(data))
}
task.resume()
//3 - return task
return task
}
}
struct Repo: Decodable {
let id: Int
let full_name: String
}
enum GithubAPI {
case getRepos(username: String)
}
extension GithubAPI {
var baseURL: URL { return URL(string: "https://api.github.com")! }
var path: String {
switch self {
case .getRepos(let username):
return "/users/\(username)/repos"
}
}
var method: HttpMethod {
switch self {
case .getRepos: return .get
}
}
var jsonParameters: [String: Any] {
return [:]
}
var headers: [String: String]? {
return ["Content-type": "application/json"]
}
}
/// Usage Example
let api = HttpService<GithubAPI>(requestClosure: { endpoint in
var request = endpoint.urlRequest()
request.setValue("Authorization", forHTTPHeaderField: "Bearer ACCESS_TOKEN")
return request
})
api.rx.request(.getRepos(username: "antonyalkmim"))
.map(Repo.self)
.subscribe(onSuccess: { repos in
print(token)
})
@antonyalkmim
Copy link
Copy Markdown
Author

antonyalkmim commented Feb 8, 2018

TODO

  • Add sampleData responses
  • Add download tasks with progress

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment