Last active
May 19, 2019 04:29
-
-
Save pofat/8a5a7083dd5b0de151a7954e7383f42e to your computer and use it in GitHub Desktop.
Implement basic network request with both protocol and generic struct
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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