Created
February 19, 2016 16:55
-
-
Save MosheBerman/1fbc4d220cd195ef31be to your computer and use it in GitHub Desktop.
Submission Manager
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 UIKit | |
class SubmissionManager: NSObject { | |
static let sharedManager = SubmissionManager() | |
var serverURLComponents : NSURLComponents = NSURLComponents() | |
private let keyDefaultsServerAddress : String = "com.company.serverURL" | |
private override init() { | |
super.init() | |
NSUserDefaults.standardUserDefaults().registerDefaults([ | |
keyDefaultsServerAddress : [ | |
"scheme" : "http", | |
"host" : "192.168.1.5", | |
"port" : NSNumber(int: 8000), | |
"path" : "/api/submit/" | |
] | |
]) | |
if let components = self.loadURLComponents() { | |
self.serverURLComponents = components | |
} | |
} | |
// MARK: - Server URL Settings | |
/** | |
Loads the most recent server URL from the defaults store. | |
*/ | |
func loadURLComponents() -> NSURLComponents? { | |
var returnComponents : NSURLComponents? = nil | |
if let serverComponentsDictionary : [String : AnyObject] = NSUserDefaults.standardUserDefaults().dictionaryForKey(keyDefaultsServerAddress) { | |
let components : NSURLComponents = NSURLComponents() | |
components.scheme = serverComponentsDictionary["scheme"] as? String | |
components.host = serverComponentsDictionary["host"] as? String | |
components.port = serverComponentsDictionary["port"] as? NSNumber | |
components.path = serverComponentsDictionary["path"] as? String | |
returnComponents = components | |
} | |
return returnComponents | |
} | |
/** | |
Saves the components to NSUserDefaults. | |
- parameter components: The URL components to save. | |
*/ | |
func saveURLComponents(urlComponents components: NSURLComponents) { | |
var componentsToSave : [String : AnyObject] = [:] | |
if let scheme = components.scheme, let host = components.host, let port = components.port, let path = components.path { | |
componentsToSave["scheme"] = scheme | |
componentsToSave["host"] = host | |
componentsToSave["port"] = port | |
componentsToSave["path"] = path | |
} | |
NSUserDefaults.standardUserDefaults().setObject(componentsToSave, forKey: keyDefaultsServerAddress) | |
self.serverURLComponents = components | |
} | |
// MARK: - Uploading a Submission | |
/** | |
Uploads a PDF to the server. | |
*/ | |
func uploadPDF(pdf data: NSData, withName name: String, andHandler handler: (response : NSURLResponse?, error : NSError?) -> Void) { | |
guard let payload = self.payload(withData: data, andName: name) else { | |
let error = NSError(domain: "com.company.product", code: -3, userInfo: ["reason" : "Failed to convert data into payload."]) | |
handler(response: nil, error: error) | |
return | |
} | |
self.uploadData(payload, withName: name, andHandler: handler) | |
} | |
/** | |
Uploads a Dictionary to the server. | |
*/ | |
func uploadJSON(submission : NSDictionary, withName name: String, andHandler handler: (response : NSURLResponse?, error : NSError?) -> Void) { | |
print("(Product) : JSON Submission started...") | |
// Step 1. Prepare the data for upload. | |
guard let payload = self.payload(withDictionary: submission, andName:name) else { | |
let error = NSError(domain: "com.company.product", code: -2, userInfo: ["reason" : "Failed to convert dictionary into payload."]) | |
handler(response: nil, error: error) | |
return | |
} | |
self.uploadData(payload, withName: name, andHandler: handler) | |
} | |
/** | |
Uploads a prepared payload to the server. | |
*/ | |
private func uploadData(payload : NSData, withName name: String, andHandler handler: (response : NSURLResponse?, error : NSError?) -> Void) { | |
print("(Product) : Payload prepared (\(payload.length) bytes).") | |
// Step 2. Prepare the URL | |
guard let url = self.serverURLComponents.URL else { | |
print("(Product) : URL generation failed.") | |
handler(response: nil, error: NSError(domain: "com.company.product", code: -1, userInfo: ["reason" : "Failed to create url from components."])) | |
return | |
} | |
print("(Product) : URL generated, preparing request.") | |
// Required for successful submission. | |
let boundary : String = "----------SwIfTeRhTtPrEqUeStBoUnDaRy" | |
let request : NSMutableURLRequest = NSMutableURLRequest(URL: url) | |
request.HTTPMethod = "POST" | |
request.HTTPBody = payload | |
request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type") | |
request.setValue("\(payload.length)", forHTTPHeaderField: "Content-Length") | |
let session = NSURLSession.sharedSession() | |
let uploadTask = session.uploadTaskWithRequest(request, fromData: payload, completionHandler: { (responseData: NSData?, response : NSURLResponse?, error : NSError?) -> Void in | |
print("(Product) : Upload completed, processing response...") | |
handler(response: response, error: error) | |
if let data = responseData { | |
do { | |
let responseDictionary = try NSJSONSerialization.JSONObjectWithData(data, options: []) | |
print("(Product) Response: \(responseDictionary)") | |
} | |
catch let e as NSError | |
{ | |
print("(Product) Error unwrapping response : \(e)") | |
print("\(NSString(data: data, encoding: NSUTF8StringEncoding))") | |
} | |
} | |
else | |
{ | |
print("(Product) : Didn't receive a response. \(error)") | |
} | |
print("(Product) Done.") | |
}) | |
print("(Product) : Task prepared.") | |
uploadTask.resume() | |
} | |
// MARK: - Test Server Connection | |
func testServerConnection() { | |
if let task = self.testTask() { | |
task.resume() | |
} | |
} | |
func testTask() -> NSURLSessionDataTask? { | |
let components = NSURLComponents() | |
components.scheme = self.serverURLComponents.scheme | |
components.host = self.serverURLComponents.host | |
components.port = self.serverURLComponents.port | |
components.path = "/" | |
guard let url = components.URL else { | |
print("Failed to build URL from components. \(components)") | |
return nil | |
} | |
let request = NSURLRequest(URL: url) | |
let task = NSURLSession(configuration: NSURLSessionConfiguration.defaultSessionConfiguration()).dataTaskWithRequest(request, completionHandler: { (data : NSData?, response : NSURLResponse?, error : NSError?) -> Void in | |
var success : Bool = false | |
if let data = data { | |
if let responseString : NSString = NSString(data: data, encoding: NSUTF8StringEncoding) { | |
if responseString == "Hello world." { | |
success = true | |
} | |
} | |
} | |
var results : String | |
if success { | |
results = "Successfully connected to server at '\(url)'." | |
} | |
else | |
{ | |
results = "Connection failed to server at '\(url)'." | |
} | |
let alert : UIAlertController = UIAlertController(title: "", message: "\(results)", preferredStyle: UIAlertControllerStyle.Alert) | |
let cancel = UIAlertAction(title: "OK", style: .Cancel, handler: { (action : UIAlertAction) -> Void in | |
if let rootViewController = UIApplication.sharedApplication().keyWindow?.rootViewController { | |
rootViewController.dismissViewControllerAnimated(true, completion: nil) | |
} | |
}) | |
alert.addAction(cancel) | |
dispatch_async(dispatch_get_main_queue(), { () -> Void in | |
if let rootViewController = UIApplication.sharedApplication().keyWindow?.rootViewController { | |
rootViewController.presentViewController(alert, animated: true, completion: { () -> Void in | |
}) | |
} | |
}) | |
}) | |
return task | |
} | |
// MARK: - Converting a submission dictionary into data. | |
/** | |
Converts a submission into a data payload for upload to server. | |
*/ | |
func payload(withDictionary dictionary : NSDictionary, andName name:String) -> NSData? { | |
var returnData : NSData? = nil | |
do { | |
guard let data : NSData = try NSJSONSerialization.dataWithJSONObject(dictionary, options: .PrettyPrinted) else { | |
return nil | |
} | |
returnData = self.payload(withData: data, andName: name) | |
} | |
catch let e as NSError { | |
print("Error converting dictionary to data:\(e)") | |
} | |
return returnData | |
} | |
func payload(withData data : NSData, andName name:String) -> NSData? { | |
var returnData : NSData? = nil | |
let boundary : String = "----------SwIfTeRhTtPrEqUeStBoUnDaRy" | |
let contentDisposition : String = "Content-Disposition: form-data; name=\"\(name)\"; filename=\"\(name)\"\r\n" | |
let contentType : String = "Content-Type: application/octet-stream\r\n\r\n" | |
let payload : NSMutableData = NSMutableData() | |
if let encodedBoundary = ("--\(boundary)\r\n").dataUsingEncoding(NSUTF8StringEncoding), | |
let metadata1 = contentDisposition.dataUsingEncoding(NSUTF8StringEncoding), | |
let metadata2 = contentType.dataUsingEncoding(NSUTF8StringEncoding), | |
let trailingNewlineData = "\r\n\r\n".dataUsingEncoding(NSUTF8StringEncoding) | |
{ | |
payload.appendData(encodedBoundary) | |
payload.appendData(metadata1) | |
payload.appendData(metadata2) | |
payload.appendData(data) | |
payload.appendData(trailingNewlineData) | |
payload.appendData(encodedBoundary) | |
} | |
returnData = payload | |
return returnData | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment