Skip to content

Instantly share code, notes, and snippets.

@Gujci
Last active June 7, 2016 12:37
Show Gist options
  • Select an option

  • Save Gujci/d639df15584f24eca38ff48d9eff6eb9 to your computer and use it in GitHub Desktop.

Select an option

Save Gujci/d639df15584f24eca38ff48d9eff6eb9 to your computer and use it in GitHub Desktop.
//
// API.swift
//
// Created by Gujgiczer Máté on 19/03/16.
//
import Foundation
public enum APIError: Int, ErrorType {
case Unknown
case NotFound
case Unouthorized
case Forbidden
case Timeout
case MultipleChoice
case BadRequest
init?(withResponse response: NSURLResponse?) {
if let statusCode = (response as? NSHTTPURLResponse)?.statusCode {
switch statusCode {
case 200:
return nil
case 300:
self = .MultipleChoice
case 400:
self = .BadRequest
case 401:
self = .Unouthorized
case 403:
self = .Forbidden
case 404:
self = .NotFound
case 500...599:
self = .Timeout
default:
self = .Unknown
}
}
else {
return nil
}
}
}
enum ValidJSONObjectParseError: ErrorType {
case JSONSerializeError
}
protocol ValidJSONObject {
func JSONFormat() throws -> NSData
}
enum AuthenticationType {
case None
case HTTPHeader
case URLParameter
}
class RequestAuthenticator {
var type: AuthenticationType = .None
var accessToken: String?
var tokenKey: String?
func authenticateURLRequest(req: NSMutableURLRequest) -> NSMutableURLRequest {
switch self.type {
case .HTTPHeader where accessToken != nil && tokenKey != nil:
req.addValue(accessToken!,forHTTPHeaderField: tokenKey!)
return req
case .URLParameter where accessToken != nil && tokenKey != nil:
req.URL = NSURL(url: req.URL!,query: [tokenKey!: accessToken!])
return req
default:
return req
}
}
}
typealias APICompletionHandler = ((JSON?) throws -> Void)?
class API {
var authentication: RequestAuthenticator
var baseURL: String
func post(endpoint: String, query: Dictionary<String, Queryable>? = nil, data: ValidJSONObject? = nil,
completion: (error: APIError?, object: JSON?) -> ()) {
dataTask(clientURLRequest(endpoint, query: query, params: data), method: "POST", completion: completion)
}
func put(endpoint: String, query: Dictionary<String, Queryable>? = nil, data: ValidJSONObject? = nil,
completion: (error: APIError?, object: JSON?) -> ()) {
dataTask(clientURLRequest(endpoint, query: query, params: data), method: "PUT", completion: completion)
}
func get(endpoint: String, query: Dictionary<String, Queryable>? = nil, data: ValidJSONObject? = nil,
completion: (error: APIError?, object: JSON?) -> ()) {
dataTask(clientURLRequest(endpoint, query: query, params: data), method: "GET", completion: completion)
}
func delete(endpoint: String, query: Dictionary<String, Queryable>? = nil, data: ValidJSONObject? = nil,
completion: (error: APIError?, object: JSON?) -> ()) {
dataTask(clientURLRequest(endpoint, query: query, params: data), method: "DELETE", completion: completion)
}
internal func dataTask(request: NSMutableURLRequest, method: String, completion: (error: APIError?, object: JSON?) -> ()) {
request.HTTPMethod = method
let session = NSURLSession(configuration: NSURLSessionConfiguration.defaultSessionConfiguration())
session.dataTaskWithRequest(authentication.authenticateURLRequest(request)) { (data, response, error) -> Void in
if let validData = data {
completion(error: APIError(withResponse: response), object: JSON(data: validData))
}
else {
completion(error: APIError(withResponse: response), object: nil)
}
}.resume()
}
internal func clientURLRequest(path: String,query: Dictionary<String, Queryable>?, params: ValidJSONObject?)
-> NSMutableURLRequest {
let request = NSMutableURLRequest(URL: NSURL(string: baseURL + path, query: query))
if let params = params {
let jsonData = try? params.JSONFormat()
request.HTTPBody = jsonData
}
request.addValue("application/json",forHTTPHeaderField: "Content-Type")
request.addValue("application/json",forHTTPHeaderField: "Accept")
request.addValue(NSBundle.mainBundle().preferredLocalizations.first ?? "en",forHTTPHeaderField: "Accept-Language")
return request
}
init(withBaseUrl baseUrl: String, authentication: RequestAuthenticator? = nil) {
self.baseURL = baseUrl
if let givenAuthentication = authentication {
self.authentication = givenAuthentication
}
else {
self.authentication = RequestAuthenticator()
}
}
}
protocol Queryable {
func queryString(forKey key: String) -> [NSURLQueryItem]
}
extension Array: Queryable {
func queryString(forKey key: String) -> [NSURLQueryItem] {
return self.map() { item in
return NSURLQueryItem(name: "\(key)[]", value: "\(item)")
}
}
}
extension String: Queryable {
func queryString(forKey key: String) -> [NSURLQueryItem] {
return [NSURLQueryItem(name: key, value: self)]
}
}
extension NSURL {
convenience init(string: String, query: Dictionary<String, Queryable>?) {
if query == nil {
self.init(string: string)!
return
}
let components = NSURLComponents(string: string)
var querryItems = components?.queryItems ?? Array<NSURLQueryItem>()
query?.forEach() {
querryItems.appendContentsOf($0.1.queryString(forKey: $0.0))
}
components?.queryItems = querryItems
self.init(string: "",relativeToURL: components!.URL)!
}
convenience init(url: NSURL, query: Dictionary<String, Queryable>?) {
let components = NSURLComponents(URL: url, resolvingAgainstBaseURL: false)
var querryItems = components?.queryItems ?? Array<NSURLQueryItem>()
query?.forEach() {
querryItems.appendContentsOf($0.1.queryString(forKey: $0.0))
}
components?.queryItems = querryItems
self.init(string: "",relativeToURL: components!.URL)!
}
}
extension NSDate {
static var timestamp: Double {
get {
return NSDate().timeIntervalSince1970 * 1000
}
}
}
extension Dictionary: ValidJSONObject {
func JSONFormat() throws -> NSData {
if let serializableData = self as? AnyObject {
return try NSJSONSerialization.dataWithJSONObject(serializableData, options: .PrettyPrinted)
}
else {
throw ValidJSONObjectParseError.JSONSerializeError
}
}
}
extension Array: ValidJSONObject {
func JSONFormat() throws -> NSData {
if let serializableData = self as? AnyObject {
return try NSJSONSerialization.dataWithJSONObject(serializableData, options: .PrettyPrinted)
}
else {
throw ValidJSONObjectParseError.JSONSerializeError
}
}
}
func + <K, V>(left: Dictionary<K, V>, right: Dictionary<K, V>) -> Dictionary<K, V> {
var map = Dictionary<K, V>()
for (k, v) in left {
map[k] = v
}
for (k, v) in right {
map[k] = v
}
return map
}
func += <K, V> (inout left: Dictionary<K, V>, right: Dictionary<K, V>) -> Dictionary<K, V> {
for (k, v) in right {
left[k] = v
}
return left
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment