Last active
March 9, 2019 11:56
-
-
Save martin-lalev/20ca6fe0d1387de25374177c71a5963d to your computer and use it in GitHub Desktop.
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
// Request descriptor | |
public struct Request { | |
public let methodName: String | |
public let path: String | |
public let query: [URLQueryItem] | |
public let body: RequestBody | |
public let contentType: String? | |
public let headers: [String: String]? | |
public let timeout: TimeInterval? | |
public let cachePolicy: NSURLRequest.CachePolicy? | |
public init(_ methodName: String, path: String, query: [URLQueryItem] = [], body: RequestBody = .none, headers: [String: String]? = [:], contentType: String? = nil, timeout: TimeInterval? = nil, cachePolicy: NSURLRequest.CachePolicy? = nil) { | |
self.methodName = methodName | |
self.path = path | |
self.query = query | |
self.body = body | |
self.headers = headers | |
self.contentType = contentType ?? body.contentType | |
self.timeout = timeout | |
self.cachePolicy = cachePolicy | |
} | |
} | |
// Request body descriptor | |
public enum RequestBody { | |
case none | |
case plain(data: Data?, contentType: String?) | |
case json(parameters: [String:Any]) | |
case urlEncoded(parameters: [URLQueryItem]) | |
public var contentType: String? { | |
switch self { | |
case .none: | |
return nil | |
case .plain(_, let contentType): | |
return contentType | |
case .json: | |
return "application/json" | |
case .urlEncoded: | |
return "application/x-www-form-urlencoded" | |
} | |
} | |
public func rawData() -> Data? { | |
switch self { | |
case .none: | |
return nil | |
case .plain(let data, _): | |
return data | |
case .json(let parameters): | |
return try? JSONSerialization.data(withJSONObject: parameters, options: []) | |
case .urlEncoded(let parameters): | |
return parameters.map({ $0.name + "=" + ($0.value ?? "") }).joined(separator: "&").data(using: .utf8) | |
} | |
} | |
} | |
// Common request builders | |
extension Request { | |
public static func get(path: String, query:[URLQueryItem] = [], headers:[String: String]? = [:], contentType: String? = nil, timeout: TimeInterval? = nil, cachePolicy: NSURLRequest.CachePolicy? = nil) -> Request { | |
return Request("GET", path: path, query: query, body: RequestBody.none, headers: headers, contentType: contentType, timeout: timeout, cachePolicy: cachePolicy) | |
} | |
public static func post(path: String, body: RequestBody, headers:[String: String]? = [:], contentType: String? = nil, timeout: TimeInterval? = nil, cachePolicy: NSURLRequest.CachePolicy? = nil) -> Request { | |
return Request("POST", path: path, body: body, headers: headers, contentType: contentType, timeout: timeout, cachePolicy: cachePolicy) | |
} | |
public static func put(path: String, body: RequestBody, headers:[String: String]? = [:], contentType: String? = nil, timeout: TimeInterval? = nil, cachePolicy: NSURLRequest.CachePolicy? = nil) -> Request { | |
return Request("PUT", path: path, body: body, headers: headers, contentType: contentType, timeout: timeout, cachePolicy: cachePolicy) | |
} | |
public static func delete(path: String, body: RequestBody = .none, headers:[String: String]? = [:], contentType: String? = nil, timeout: TimeInterval? = nil, cachePolicy: NSURLRequest.CachePolicy? = nil) -> Request { | |
return Request("DELETE", path: path, body: body, headers: headers, contentType: contentType, timeout: timeout, cachePolicy: cachePolicy) | |
} | |
public static func patch(path: String, body: RequestBody, headers:[String: String]? = [:], contentType: String? = nil, timeout: TimeInterval? = nil, cachePolicy: NSURLRequest.CachePolicy? = nil) -> Request { | |
return Request("PATCH", path: path, body: body, headers: headers, contentType: contentType, timeout: timeout, cachePolicy: cachePolicy) | |
} | |
} | |
// Convertion to URLRequest | |
extension Request { | |
public func generateRequest(host:String, defaultHeaders:[String: String] = [:], timeout:TimeInterval = 60, cachePolicy:NSURLRequest.CachePolicy = .reloadIgnoringCacheData) -> URLRequest { | |
let url: URL = { | |
var urlComponents = URLComponents(string: host) | |
urlComponents?.path = "/" + self.path | |
urlComponents?.queryItems = self.query | |
return urlComponents?.url | |
}()! | |
var request = URLRequest(url: url, cachePolicy: self.cachePolicy ?? cachePolicy, timeoutInterval: self.timeout ?? timeout) | |
request.httpMethod = self.methodName | |
for header in defaultHeaders { | |
request.setValue(header.value, forHTTPHeaderField: header.key) | |
} | |
for header in self.headers ?? [:] { | |
request.setValue(header.value, forHTTPHeaderField: header.key) | |
} | |
if let contentType = self.contentType { | |
request.setValue(contentType, forHTTPHeaderField: "Content-Type") | |
} | |
if let bodyData = self.body.rawData() { | |
request.httpBody = bodyData | |
request.setValue(String(describing:bodyData.count), forHTTPHeaderField: "Content-Length") | |
} | |
return request | |
} | |
} | |
// Cookies helper | |
extension Request { | |
public static func cookieHeaders(for cookies: [String: String], originURL url:URL) -> [String:String] { | |
return HTTPCookie.requestHeaderFields(with: cookies.reduce([]) { (result, cookie) -> [HTTPCookie] in | |
if let httpCookie = HTTPCookie(properties: [HTTPCookiePropertyKey.originURL:url, HTTPCookiePropertyKey.path:"\\", HTTPCookiePropertyKey.name:cookie.key, HTTPCookiePropertyKey.value: cookie.value]) { | |
return result + [httpCookie] | |
} else { | |
return result | |
} | |
}) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Usage example: