Last active
February 10, 2016 14:19
-
-
Save karl-gustav/29274e75bf63ca6e67d0 to your computer and use it in GitHub Desktop.
HTTP request wrapper for swift. It's biggest advantage is that you can replay requests `let r = MyRequest.get("url"); while true { r.makeRequest() };`
This file contains hidden or 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 UIKit | |
class MyRequest { | |
class func get(url:String) -> RequestHandler { | |
return RequestHandler(url: url, method: "GET") | |
} | |
class func post(url:String) -> RequestHandler { | |
return RequestHandler(url: url, method: "POST") | |
} | |
class func put(url:String) -> RequestHandler { | |
return RequestHandler(url: url, method: "PUT") | |
} | |
class func delete(url:String) -> RequestHandler { | |
return RequestHandler(url: url, method: "DELETE") | |
} | |
} | |
class RequestHandler { | |
private let _method: String | |
private var _headers = [String:String]() | |
private var _body: NSData? | |
private var _auth: String? | |
private let _url: NSURL | |
private var _boundary: String? | |
private var errorPlaceholder: NSError? | |
private static var numberOfOpenRequests: Int = 0 | |
private static var sharedApplication:UIApplication? { | |
// hack we have to do because UIApplication.sharedApplication is not available in the Apple Watch framework | |
#if TARGET_MAIN_APP | |
return UIApplication.sharedApplication() | |
#else | |
return nil | |
#endif | |
} | |
enum types { | |
case JSON, MULTIPART, URL_ENCODED | |
} | |
init (url: String, method: String) { | |
_url = NSURL(string: url)! | |
_method = method | |
} | |
func extraHeaders(headers: [String:String]) -> RequestHandler { | |
for (key, value) in headers { | |
if value != "Content-Type" && value != "Accept" { | |
_headers[key] = value | |
} | |
} | |
return self | |
} | |
func multipartBody (data: NSData, boundary: String) -> RequestHandler { | |
_headers["Content-Type"] = "multipart/form-data; boundary=\(boundary)" | |
_body = data | |
return self | |
} | |
func jsonBody (data: AnyObject) -> RequestHandler { | |
_headers["Content-Type"] = "application/json" | |
if NSJSONSerialization.isValidJSONObject(data) { | |
let bodyString = RequestHandler.JSONStringify(data, errorPointer: &errorPlaceholder) | |
_body = bodyString.dataUsingEncoding(NSUTF8StringEncoding) | |
} | |
else { | |
NSException(name: "WRONG_INPUT_TO_JSON_BODY", reason: "You gave it an object that couldn't be parsed to JSON", userInfo: nil).raise() | |
} | |
return self | |
} | |
func urlEncodeBody (data: [String: String]) -> RequestHandler { | |
_headers["Content-Type"] = "application/x-www-form-urlencoded" | |
var parts = [String]() | |
for (key,val) in data { | |
parts.append(String(format:"%@=%@", key, val)) | |
} | |
_body = parts.joinWithSeparator("&").dataUsingEncoding(NSUTF8StringEncoding) | |
return self | |
} | |
func basicAuth(username username: String, password: String) -> RequestHandler { | |
let loginString = NSString(format: "%@:%@", username, password) | |
let loginData: NSData = loginString.dataUsingEncoding(NSUTF8StringEncoding)! | |
let base64EncodedCredentials = loginData.base64EncodedStringWithOptions([]) | |
_headers["Authorization"] = "Basic \(base64EncodedCredentials)" | |
return self | |
} | |
func bearerAuth(bearer: String) -> RequestHandler { | |
_headers["Authorization"] = "Bearer \(bearer)" | |
return self | |
} | |
func makeRequest(successHandler successHandler: ([String: AnyObject], NSHTTPURLResponse) -> Void, errorHandler: (NSError, NSHTTPURLResponse?) -> Void) -> RequestHandler { | |
self.dynamicType.increaseOpenConnectionCount() | |
var errorPlaceholder: NSError? | |
let request = NSMutableURLRequest(URL: _url) | |
request.HTTPMethod = _method | |
let headers = RequestHandler.addDefaultHeaders(_headers) | |
for (key, value) in headers { | |
request.setValue(value, forHTTPHeaderField: key) | |
} | |
if _body != nil { | |
request.HTTPBody = _body | |
} | |
if errorPlaceholder != nil { | |
errorHandler(errorPlaceholder!, nil) | |
return self | |
} | |
let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, response, error in | |
self.dynamicType.decreaseOpenConnectionCount() | |
if error != nil { | |
errorHandler(error!, nil) | |
return | |
} | |
let urlResponse = response as! NSHTTPURLResponse | |
if urlResponse.statusCode >= 200 && urlResponse.statusCode < 300 { // 200..299 | |
let dataAsString = NSString(data: data!, encoding: NSUTF8StringEncoding) as! String | |
// This is a patch for the Assignments' API. It returns a 201 but with empty body | |
if dataAsString.characters.count == 0 { | |
successHandler([String: AnyObject](), urlResponse) | |
return | |
} | |
let responseDict = RequestHandler.JSONParseStringToDictionary(dataAsString, errorPointer: &errorPlaceholder) | |
if errorPlaceholder != nil { | |
errorHandler(errorPlaceholder!, urlResponse) | |
return | |
} | |
successHandler(responseDict, urlResponse) | |
} | |
else { | |
let url = urlResponse.URL?.absoluteString ?? "url(N/A)" | |
let err = NSError(domain: "MyRequest.RequestHandler", code: -1, userInfo: [ | |
NSLocalizedDescriptionKey: "Got \(urlResponse.statusCode) status code on request to \(url)", | |
"statusCode": urlResponse.statusCode, | |
"url": url, | |
"body": NSString(data: data!, encoding: NSUTF8StringEncoding) as! String | |
]) | |
errorHandler(err, urlResponse) | |
} | |
} | |
task.resume() | |
return self | |
} | |
private class func addDefaultHeaders(headers: [String:String]) -> [String:String] { | |
var mutableHeaders = headers | |
mutableHeaders["Accept"] = "application/json" // This class only supports JSON as return type | |
mutableHeaders["User-Agent"] = Helper.userAgent() | |
return mutableHeaders | |
} | |
private class func JSONStringify(value: AnyObject = "", errorPointer: NSErrorPointer = nil) -> String { | |
if NSJSONSerialization.isValidJSONObject(value) { | |
do { | |
let data = try NSJSONSerialization.dataWithJSONObject(value, options: []) | |
if let string = NSString(data: data, encoding: NSUTF8StringEncoding) { | |
return string as String | |
} | |
} catch let error as NSError { | |
errorPointer.memory = error | |
} | |
} | |
else { | |
errorPointer.memory = NSError(domain: "MyRequest.RequestHandler", code: -2, userInfo: [ | |
NSLocalizedDescriptionKey: "Got an object that can't be converted to json: \(value)" | |
]) | |
} | |
return "" | |
} | |
private class func JSONParseStringToDictionary(jsonString: String = "", errorPointer: NSErrorPointer = nil) -> [String: AnyObject] { | |
if let data = jsonString.dataUsingEncoding(NSUTF8StringEncoding) { | |
do { | |
if let dictionary = try NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(rawValue: 0)) as? [String: AnyObject] { | |
return dictionary | |
} | |
} | |
catch _ { | |
} | |
} | |
return [String: AnyObject]() | |
} | |
private class func increaseOpenConnectionCount() { | |
numberOfOpenRequests++ | |
sharedApplication?.networkActivityIndicatorVisible = true | |
} | |
private class func decreaseOpenConnectionCount() { | |
if --numberOfOpenRequests <= 0 { | |
numberOfOpenRequests = 0 | |
sharedApplication?.networkActivityIndicatorVisible = false | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Usage