Skip to content

Instantly share code, notes, and snippets.

@robertBojor
Last active September 21, 2015 19:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save robertBojor/40b4b5e3fe93a11bda24 to your computer and use it in GitHub Desktop.
Save robertBojor/40b4b5e3fe93a11bda24 to your computer and use it in GitHub Desktop.
ApiManager for Swift v2.0
//
// 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