ApiManager for Swift v2.0
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
// | |
// APIManager.swift | |
// | |
// Created by Robert Bojor on 21/09/15. | |
// Copyright (c) 2015 Robert Bojor. All rights reserved. | |
// | |
import Foundation | |
public class ApiManager: NSObject { | |
let localStore = NSUserDefaults.standardUserDefaults() | |
var errorDomain:String = "com.yourdomain.api" | |
var authHeaderName = "X-Authorization" | |
var authFieldInLocalStorage = "authToken" | |
let apiBaseURL = "https://api.yourdomain.com/" | |
let apiVersionStub = "v1/" | |
/* Endpoint format: METHOD~URL_STUB with possible parameters expressed as ? */ | |
let kEndpointAuth = "POST~auth" | |
let kEndpointUserAdd = "POST~users" | |
let kEndpointUserGetDetails = "GET~users/?" | |
let kEndpointUserUpdateDetails = "POST~users/?" | |
public enum ErrorCodes:Int { | |
case AuthFailed = 0, | |
AuthMissing = 1, | |
UnknownErrorOnServer = 2, | |
ServerISEError = 3 | |
} | |
public enum ErrorMessages:String { | |
case | |
EmptyAPIURL = "API URL cannot be empty", | |
EmptyAPIVersion = "API version cannot be empty", | |
CheckingForNetwork = "Checking if connected to network", | |
NotConnected = "Not connected to the internet", | |
ConnectedToInternet = "Connected to internet", | |
EmptyCredentials = "Authentication credentials cannot be empty", | |
AuthTokenNotFound = "authToken was not found", | |
AuthTokenFound = "authToken found and saved" | |
} | |
public enum MimeTypes:String { | |
case ApplicationAtomXML = "application/atom+xml", | |
ApplicationVnd = "application/vnd.dart", | |
ApplicationECMAScript = "application/ecmascript", | |
ApplicationEDIX12 = "application/EDI-X12", | |
ApplicationEDIFACT = "application/EDIFACT", | |
ApplicationJSON = "application/json", | |
ApplicationJavaScript = "application/javascript", | |
ApplicationOctetStream = "application/octet-stream", | |
ApplicationOGG = "application/ogg", | |
ApplicationDashXML = "application/dash+xml", | |
ApplicationPDF = "application/pdf", | |
ApplicationPostScript = "application/postscript", | |
ApplicationRDF = "application/rdf+xml", | |
ApplicationRSS = "application/rss+xml", | |
ApplicationSOAP = "application/soap+xml", | |
ApplicationWOFF = "application/font-woff", | |
ApplicationXHTML = "application/xhtml+xml", | |
ApplicationXML = "application/xml", | |
ApplicationDTD = "application/xml-dtd", | |
ApplicationXOP = "application/xop+xml", | |
ApplicationZIP = "application/zip", | |
ApplicationGzip = "application/gzip", | |
ApplicationNACL = "application/x-nacl", | |
ApplicationPNACL = "application/x-pnacl", | |
ApplicationSMIL = "application/smil+xml", | |
AudioBasic = "audio/basic", | |
AudioL24 = "audio/L24", | |
AudioMP4 = "audio/mp4", | |
AudioMPEG = "audio/mpeg", | |
AudioOGG = "audio/ogg", | |
AudioFlac = "audio/flac", | |
AudioOpus = "audio/opus", | |
AudioVorbis = "audio/vorbis", | |
AudioRealAudio = "audio/vnd.rn-realaudio", | |
AudioWAV = "audio/vnd.wave", | |
AudioWebM = "audio/webm", | |
ImageGIF = "image/gif", | |
ImageJPEG = "image/jpeg", | |
ImagePJPEG = "image/pjpeg", | |
ImagePNG = "image/png", | |
ImageSVG = "image/svg+xml", | |
ImageTiff = "image/tiff", | |
ImageDjVu = "image/vnd.djvu", | |
MessageHTTP = "message/http", | |
MessageIMDN = "message/imdn+xml", | |
MessagePartial = "message/partial", | |
MessageRFC822 = "message/rfc822", | |
ModelIGS = "model/iges", | |
ModelMesh = "model/mesh", | |
ModelVRML = "model/vrml", | |
ModelX3DISO = "model/x3d+binary", | |
ModelX3DFastInfoSet = "model/x3d+fastinfoset", | |
ModelX3DVRML = "model/x3d-vrml", | |
ModelX3DXML = "model/x3d+xml", | |
MultipartMixed = "multipart/mixed", | |
MultipartAlternative = "multipart/alternative", | |
MultipartRelated = "multipart/related", | |
MultipartFormData = "multipart/form-data", | |
MultipartSigned = "multipart/signed", | |
MultipartEncrypted = "multipart/encrypted", | |
TextCMD = "text/cmd", | |
TextCSS = "text/css", | |
TextCSV = "text/csv", | |
TextHTML = "text/html", | |
TextPlain = "text/plain", | |
TextRTF = "text/rtf", | |
TextvCard = "text/vcard", | |
TextALanguage = "text/vnd.a", | |
TextABCNotation = "text/vnd.abc", | |
TextXML = "text/xml", | |
VideoAVI = "video/avi", | |
VideoMPEG = "video/mpeg", | |
VideoMP4 = "video/mp4", | |
VideoOGG = "video/ogg", | |
VideoQuicktime = "video/quicktime", | |
VideoWebM = "video/webm", | |
VideoMatroska = "video/x-matroska", | |
VideoWMV = "video/x-ms-wmv", | |
VideoFLV = "video/x-flv" | |
} | |
func NSURLByAppendingQueryParameters(URL : NSURL!, queryParameters : Dictionary<String, String>) -> NSURL { | |
let URLString : NSString = NSString(format: "%@?%@", URL.absoluteString, self.stringFromQueryParameters(queryParameters)) | |
return NSURL(string: URLString as String)! | |
} | |
func stringFromQueryParameters(queryParameters : Dictionary<String, String>) -> String { | |
var parts:[String] = [] | |
for (name, value) in queryParameters { | |
let part = name.stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.URLFragmentAllowedCharacterSet())! + "=" + value.stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.URLFragmentAllowedCharacterSet())! | |
parts.append(part) | |
} | |
return parts.joinWithSeparator("&") | |
} | |
func dataToJSONObject(data:NSData) -> NSDictionary { | |
var jsonObject = NSDictionary() | |
do { | |
jsonObject = try NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.AllowFragments) as! NSDictionary | |
} catch { | |
jsonObject = ["payload":NSString(data: data, encoding: NSUTF8StringEncoding) as! String] | |
} | |
return jsonObject | |
} | |
public func sendRequest(toEndpoint:String, withURLParams:Array<String>?, withAuthentication:Bool, withHeaders:Dictionary<String,String>?, withParameters:Dictionary<String,String>?, withSuccess:(responsePayload:NSDictionary!, statusCode:Int!) -> Void, withFailure:(responsePayload:NSDictionary!, statusCode:Int, andError:NSError!)->Void) -> Void | |
{ | |
let sessionConfig = NSURLSessionConfiguration.defaultSessionConfiguration() | |
let session = NSURLSession(configuration: sessionConfig, delegate: nil, delegateQueue: nil) | |
let urlParts = toEndpoint.componentsSeparatedByString("~") | |
let requestMethod = urlParts[0] | |
var endpointURL = urlParts[1] | |
if (withURLParams != nil && withURLParams!.count > 0) { | |
for(_, val) in withURLParams!.enumerate() { | |
let occurence = endpointURL.rangeOfString("?") | |
if (occurence != nil) { | |
endpointURL = endpointURL.stringByReplacingCharactersInRange(occurence!, withString: "\(val)") | |
} | |
} | |
} | |
endpointURL = self.apiBaseURL + self.apiVersionStub + endpointURL | |
var URL = NSURL(string: endpointURL) | |
if requestMethod == "GET" && withParameters != nil && withParameters!.count > 0 { | |
URL = self.NSURLByAppendingQueryParameters(URL, queryParameters: withParameters!) | |
} | |
let request = NSMutableURLRequest(URL: URL!) | |
request.HTTPMethod = requestMethod | |
request.addValue("application/json", forHTTPHeaderField: "Accept") | |
if requestMethod == "POST" { | |
request.addValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type") | |
} | |
if (withHeaders != nil && withHeaders!.count > 0) { | |
for(key, val) in withHeaders!.enumerate() { | |
request.addValue("\(val)", forHTTPHeaderField: "\(key)") | |
} | |
} | |
if (withAuthentication) { | |
let authToken:String = localStore.objectForKey(self.authFieldInLocalStorage) as! String | |
if authToken == "" { | |
let errorCode = ErrorCodes.AuthMissing.rawValue | |
let userInfoError = ["description":"Authentication has not been performed"] | |
let authError = NSError(domain: errorDomain, code: errorCode, userInfo: userInfoError) | |
withFailure(responsePayload: nil, statusCode: 0, andError: authError) | |
} | |
request.addValue(authToken, forHTTPHeaderField: self.authHeaderName) | |
} | |
if requestMethod == "POST" && withParameters != nil && withParameters!.count > 0 { | |
let queryParameters = stringFromQueryParameters(withParameters!) | |
request.HTTPBody = queryParameters.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: true) | |
} | |
let task = session.dataTaskWithRequest(request, completionHandler: { (data : NSData?, response : NSURLResponse?, error : NSError?) -> Void in | |
let responseStatusCode = (response as! NSHTTPURLResponse).statusCode | |
if (error == nil) { | |
switch (responseStatusCode) { | |
// Everything ok | |
case 200: | |
withSuccess(responsePayload: self.dataToJSONObject(data!), statusCode: responseStatusCode) | |
break | |
// Bad request, Unauthorised request, Internal Server Error | |
case 400, 403, 500: | |
withFailure(responsePayload: self.dataToJSONObject(data!), statusCode: responseStatusCode, andError: error!) | |
break | |
case 500: | |
let errorCode = ErrorCodes.ServerISEError.rawValue | |
let userInfoError = ["description":"Internal Server Error - Data attached, if any"] | |
let serverError = NSError(domain: self.errorDomain, code: errorCode, userInfo: userInfoError) | |
withFailure(responsePayload: self.dataToJSONObject(data!), statusCode: 500, andError: serverError) | |
break | |
// None of the above | |
default: | |
let errorCode = ErrorCodes.UnknownErrorOnServer.rawValue | |
let userInfoError = ["description":"Unknown Server Error - Data attached, if any"] | |
let serverError = NSError(domain: self.errorDomain, code: errorCode, userInfo: userInfoError) | |
withFailure(responsePayload: self.dataToJSONObject(data!), statusCode: 0, andError: serverError) | |
break | |
} | |
} else { | |
withFailure(responsePayload: self.dataToJSONObject(data!), statusCode: responseStatusCode, andError: error!) | |
} | |
}) | |
task.resume() | |
} | |
public func sendUploadRequest(toEndpoint toEndpoint:String, withURLParams:Array<String>?, withAuthentication:Bool, withHeaders:Dictionary<String,String>?, withParameters:Dictionary<String,String>?, | |
withFilePath:String?, withFileName:String?, withFileContentType:MimeTypes?, withSuccess:(responsePayload:NSDictionary!, statusCode:Int!) -> Void, withFailure:(responsePayload:NSDictionary!, statusCode:Int, andError:NSError!)->Void) -> Void | |
{ | |
let boundary = "__RB_HELPER_BOUNDARY__" | |
var bodyString = "" | |
var separator = "" | |
var bodyData = NSMutableData() | |
let sessionConfig = NSURLSessionConfiguration.defaultSessionConfiguration() | |
let session = NSURLSession(configuration: sessionConfig, delegate: nil, delegateQueue: nil) | |
let urlParts = toEndpoint.componentsSeparatedByString("~") | |
let requestMethod = urlParts[0] | |
var endpointURL = urlParts[1] | |
if (withURLParams != nil && withURLParams!.count > 0) { | |
for(_, val) in withURLParams!.enumerate() { | |
let occurence = endpointURL.rangeOfString("?") | |
if (occurence != nil) { | |
endpointURL = endpointURL.stringByReplacingCharactersInRange(occurence!, withString: "\(val)") | |
} | |
} | |
} | |
endpointURL = self.apiBaseURL + self.apiVersionStub + endpointURL | |
let URL = NSURL(string: endpointURL) | |
let request = NSMutableURLRequest(URL: URL!) | |
request.HTTPMethod = requestMethod | |
if (withHeaders != nil && withHeaders!.count > 0) { | |
for(key, val) in withHeaders!.enumerate() { | |
request.addValue("\(val)", forHTTPHeaderField: "\(key)") | |
} | |
} | |
if (withAuthentication) { | |
let authToken:String = localStore.objectForKey(self.authFieldInLocalStorage) as! String | |
if authToken == "" { | |
let errorCode = ErrorCodes.AuthMissing.rawValue | |
let userInfoError = ["description":"Authentication has not been performed"] | |
let authError = NSError(domain: errorDomain, code: errorCode, userInfo: userInfoError) | |
withFailure(responsePayload: nil, statusCode: 0, andError: authError) | |
} | |
request.addValue(authToken, forHTTPHeaderField: self.authHeaderName) | |
} | |
if (withParameters != nil && withParameters!.count > 0) { | |
for(paramKey, paramVal) in withParameters!.enumerate() { | |
bodyString = "\(separator)--\(boundary)\r\nContent-Disposition: form-data; name=\"\(paramKey)\"\r\n\r\n\(paramVal)" | |
separator = "\r\n" | |
bodyData.appendData(bodyString.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: true)!) | |
} | |
} | |
if (withFilePath != nil) { | |
let fileData = NSData(contentsOfFile: withFilePath!) | |
if ((fileData) != nil) { | |
bodyString = "\(separator)--\(boundary)\r\nContent-Disposition: form-data; name=\"payload\"; filename=\"\(withFileName)\"\r\nContent-Type: \(withFileContentType!.rawValue)\r\n\r\n" | |
separator = "\r\n" | |
bodyData.appendData(bodyString.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: true)!) | |
bodyData.appendData(fileData!) | |
bodyData.appendData(separator.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: true)!) | |
} | |
} | |
bodyString = "--\(boundary)--\r\n" | |
bodyData.appendData(bodyString.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: true)!) | |
request.HTTPBody = bodyData | |
let task = session.dataTaskWithRequest(request, completionHandler: { (data : NSData?, response : NSURLResponse?, error : NSError?) -> Void in | |
let responseStatusCode = (response as! NSHTTPURLResponse).statusCode | |
if (error == nil) { | |
switch (responseStatusCode) { | |
// Everything ok | |
case 200: | |
withSuccess(responsePayload: self.dataToJSONObject(data!), statusCode: responseStatusCode) | |
break | |
// Bad request, Unauthorised request, Internal Server Error | |
case 400, 403, 500: | |
withFailure(responsePayload: self.dataToJSONObject(data!), statusCode: responseStatusCode, andError: error!) | |
break | |
case 500: | |
let errorCode = ErrorCodes.ServerISEError.rawValue | |
let userInfoError = ["description":"Internal Server Error - Data attached, if any"] | |
let serverError = NSError(domain: self.errorDomain, code: errorCode, userInfo: userInfoError) | |
withFailure(responsePayload: self.dataToJSONObject(data!), statusCode: 500, andError: serverError) | |
break | |
// None of the above | |
default: | |
let errorCode = ErrorCodes.UnknownErrorOnServer.rawValue | |
let userInfoError = ["description":"Unknown Server Error - Data attached, if any"] | |
let serverError = NSError(domain: self.errorDomain, code: errorCode, userInfo: userInfoError) | |
withFailure(responsePayload: self.dataToJSONObject(data!), statusCode: 0, andError: serverError) | |
break | |
} | |
} else { | |
withFailure(responsePayload: self.dataToJSONObject(data!), statusCode: responseStatusCode, andError: error!) | |
} | |
}) | |
task.resume() | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment