Skip to content

Instantly share code, notes, and snippets.

@pofat
Last active May 19, 2019 04:29
Show Gist options
  • Save pofat/8a5a7083dd5b0de151a7954e7383f42e to your computer and use it in GitHub Desktop.
Save pofat/8a5a7083dd5b0de151a7954e7383f42e to your computer and use it in GitHub Desktop.
Implement basic network request with both protocol and generic struct
import Foundation
import RxSwift
enum HTTPMethod: String {
case POST, GET
}
// An object
struct User: Decodable {
let id: String
let name: String
let gender: String
}
// ** They way of using protocol **
protocol Request {
var path: String { get }
var method: HTTPMethod { get }
var param: [String: Any] { get }
associatedtype ResponseObject: Decodable
}
// The object to send request
protocol RequestSender {
var base: String { get }
func send<T: Request>(_ r: T) -> Observable<T.ResponseObject?>
}
// Any object who can deal with HTTP request
struct URLSessionDebugRequestSender: RequestSender {
var base = "http://www.dummy.api"
func send<T: Request>(_ r: T) -> Observable<T.ResponseObject?> {
let url = URL(string: base.appending(r.path))!
var request = URLRequest(url: url)
request.httpMethod = r.method.rawValue
if let body = try? JSONSerialization.data(withJSONObject: r.param, options: .prettyPrinted) {
request.httpBody = body
}
return Observable.create { subscriber in
let task = URLSession.shared.dataTask(with: request) { data, res, error in
if let data = data, let object = try? JSONDecoder().decode(T.ResponseObject.self, from: data) {
subscriber.onNext(object)
} else {
subscriber.onError(error!)
}
}
task.resume()
return Disposables.create()
}
}
}
// A real request
struct GetUserRequest: Request {
let id: String
var path: String {
return "/users/\(id)"
}
var param: [String : Any]
let method: HTTPMethod = .GET
typealias ResponseObject = User
init(id: String, param: [String: Any] = [:]) {
self.id = id
self.param = param
}
}
// usage example
func example() {
let request = GetUserRequest(id: "a12345", param: ["foo": "bar"])
_ = URLSessionDebugRequestSender().send(request)
.subscribeOn(SerialDispatchQueueScheduler.init(qos: .background))
.observeOn(MainScheduler.instance).subscribe(onNext: { user in
if let user = user {
print("user is : \(user.name)")
} else {
print("got nothing")
}
})
}
// ** They way of using generic sturct **
struct Request<Response: Decodable> {
var endpoint: String
var method: HTTPMethod
var param: [String: Any]?
let decode: (Data) -> Response?
}
struct RequestSender {
let base = "https://foo.bar"
private let sendingRequst: (URLRequest) -> Observable<Data> // To abstract your network framework
init(_ sender: @escaping(URLRequest) -> Observable<Data>) {
self.sendingRequst = sender
}
func send<Response: Decodable>(_ r: Request<Response>) -> Observable<Response?> {
let url = URL(string: base + r.endpoint)!
var request = URLRequest(url: url)
request.httpMethod = r.method.rawValue
if let param = r.param, let body = try? JSONSerialization.data(withJSONObject: param, options: .prettyPrinted) {
request.httpBody = body
}
return sendingRequst(request).map(r.decode)
}
}
func example2() {
let usersDecode: (Data) -> [User]? = { data in
return try? JSONDecoder().decode([User].self, from: data)
}
let getUsersRequest = Request(endpoint: "/users", method: .GET, param: nil, decode: usersDecode)
// You can replace URLSession with any other network framework, such as Alamofire
let urlSessionRequestSender = RequestSender { request in
return Observable<Data>.create { subscriber in
let task = URLSession.shared.dataTask(with: request) { data, res, error in
if let data = data {
subscriber.onNext(data)
} else {
subscriber.onError(error!)
}
}
task.resume()
return Disposables.create()
}
}
// Do real deal request
_ = urlSessionRequestSender.send(getUsersRequest).subscribe(onNext: { users in
guard let users = users else {
return
}
print(users)
})
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment