Skip to content

Instantly share code, notes, and snippets.

@martin-lalev
Last active March 9, 2019 11:56
Show Gist options
  • Save martin-lalev/20ca6fe0d1387de25374177c71a5963d to your computer and use it in GitHub Desktop.
Save martin-lalev/20ca6fe0d1387de25374177c71a5963d to your computer and use it in GitHub Desktop.
// 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
}
})
}
}
@martin-lalev
Copy link
Author

Usage example:

// Simple get request
let request = Request.get(path: "api/posts")
// Get request with query parameters
let queryItems = [
    URLQueryItem(name: "userID", value: currentUserID)
]
let request = Request.get(path: "api/posts", query: queryItems)
// Post request with json
let bodyJson = ["title": postTitle, "content": postContent]
let request = Request.post(path: "api/\(currentUserID)/posts/", body: .json(parameters: bodyJson))
// Executing the request
let urlRequest = request.generateRequest(host: "http://www.example.com/")
URLSession.shared.dataTask(with: urlRequest) { (data, response, error) in
    // response handling
}

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