Layer Network common used
//
// NetworkProtocol.swift
//
// Created for test in 2019
// Created by Ajiejoy on 03/01/19 with love and sweat
//
// Reach me on self.ajiejoy@gmail.com
// Copyright © 2019 Ajiejoy. All rights reserved.
//
import UIKit
protocol NetworkProtocol : class {
var log : Network.Log {get set}
@discardableResult func request(method:Network.Method ,complete:((Network.Handler) -> Void)?) -> URLSessionDataTask?
@discardableResult func request(method:Network.Method ,header:[[String:String]],complete:((Network.Handler) -> Void)?) -> URLSessionDataTask?
@discardableResult func request(url urlRequest:URLRequest? , complete:((Network.Handler) -> Void)?) -> URLSessionDataTask?
}
struct Network {
struct Error {
var code : Int
var description : String
}
enum Handler {
case success(data:Any?)
case failed(data:Any?,error:Error)
}
enum Method {
case get(url:String)
case post(url:String,params:Any?)
case put(url:String,params:Any?)
case delete(url:String,params:Any?)
case multipart(url:String,params:Any?,parts:[Part]?)
var rawValue: String {
switch self {
case .get:
return "GET"
case .post:
return "POST"
case .put:
return "PUT"
case .delete:
return "DELETE"
case .multipart:
return "POST"
}
}
}
struct Part {
var key : String
var name : String
var data : Data?
var mimeType : String
init(key:String,data:Data?,name:String = "\(NSUUID().uuidString).jpg",mimeType:String = "image/jpeg" ) {
self.key = key
self.data = data
self.name = name
self.mimeType = mimeType
}
}
enum Log {
case none
case verbose
case error
}
}
class API : NetworkProtocol {
var log : Network.Log = .verbose
static var shared : NetworkProtocol = API()
private static var defaultHeader : [[String:String]] {
return [
["Content-Type":"application/json"],
["Accept":"application/json"]
]
}
private(set) var session : URLSession
init() {
let config = URLSessionConfiguration.default
config.httpMaximumConnectionsPerHost = 2;
config.timeoutIntervalForRequest = 30;
config.requestCachePolicy = .reloadIgnoringCacheData
config.networkServiceType = .default
config.allowsCellularAccess = true
session = URLSession(configuration: config)
session.delegateQueue.qualityOfService = .userInitiated
}
@discardableResult func request(url urlRequest: URLRequest?, complete: ((Network.Handler) -> Void)?) -> URLSessionDataTask? {
guard let srequest = urlRequest else {
let er = Network.Error(code: 999, description: "Request Failed")
complete?(.failed(data: nil, error: er))
return nil
}
let task = helperRequest(withRequest: srequest, complete: complete)
task.resume()
return task
}
@discardableResult func request(method: Network.Method, complete: ((Network.Handler) -> Void)?) -> URLSessionDataTask? {
return request(method: method, header: [], complete: complete)
}
@discardableResult func request(method: Network.Method, header: [[String : String]], complete: ((Network.Handler) -> Void)?) -> URLSessionDataTask? {
var request : URLRequest?
switch method {
case .get(let url):
guard let encod = url.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) else {
let er = Network.Error(code: 999, description: "Encoding URL Failed")
complete?(.failed(data: nil, error: er))
return nil
}
request = helperURL(string: encod, method: method.rawValue, header: header)
case .post(let url, let params),.put(let url, let params),.delete(let url, let params):
guard let encod = url.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) else {
let er = Network.Error(code: 999, description: "Encoding URL Failed")
complete?(.failed(data: nil, error: er))
return nil
}
if let params = params {
do {
let data = try JSONSerialization.data(withJSONObject: params, options:.prettyPrinted)
let bodyString = NSString(data: data, encoding: String.Encoding.utf8.rawValue)
request = helperURL(string: encod, method: method.rawValue,body: bodyString?.data(using: String.Encoding.utf8.rawValue), header: header)
} catch let error {
let er = Network.Error(code: 999, description: error.localizedDescription)
complete?(.failed(data: nil, error: er))
}
}
case .multipart(let url, let params, let parts) :
guard let encod = url.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) else {
let er = Network.Error(code: 999, description: "Encoding URL Failed")
complete?(.failed(data: nil, error: er))
return nil
}
let boundary = "----\(NSUUID().uuidString)"
var body = Data()
if let params = params as? Dictionary<String, Any> {
for dic in params.enumerated(){
body.append("--\(boundary)\r\n".data(using: .utf8)!)
body.append("Content-Disposition: form-data; name=\"\(dic.element.key)\"\r\n\r\n".data(using: .utf8)!)
body.append("\(dic.element.value)\r\n".data(using: .utf8)!)
}
}
if let parts = parts {
for part in parts {
if let data = part.data {
body.append("--\(boundary)\r\n".data(using: .utf8)!)
body.append("Content-Disposition: form-data; name=\"\(part.key)\"; filename=\"\(part.name)\"\r\n".data(using: .utf8)!)
body.append("Content-Type: \(part.mimeType)\r\n\r\n".data(using: .utf8)!)
body.append(data)
body.append("\r\n".data(using: .utf8)!)
}
}
}
body.append("--\(boundary)--\r\n".data(using: .utf8)!)
request = helperURL(string: encod, method: method.rawValue,body: body, header: header)
}
guard let srequest = request else {
let er = Network.Error(code: 999, description: "Request Failed")
complete?(.failed(data: nil, error: er))
return nil
}
let task = helperRequest(withRequest: srequest, complete: complete)
task.resume()
return task
}
private func helperURL(string:String,method:String,body:Data? = nil,header:[[String : String]]) -> URLRequest? {
guard let url = URL(string: string) else { return nil }
var request = URLRequest(url: url)
request.addValue(String(body?.count ?? 0), forHTTPHeaderField: "Content-Length")
let header = API.defaultHeader + header
header.forEach { h in
h.forEach({ (arg0) in
let (key, value) = arg0
request.setValue(value, forHTTPHeaderField: key)
})
}
request.httpMethod = method
request.httpBody = body
return request
}
private func helperRequest(withRequest request:URLRequest,complete:((Network.Handler) -> Void)?) -> URLSessionDataTask {
if log == .verbose {
print("----------Start Request----------\n","URL :",request.url?.absoluteString ?? "nil","\nMethod :",request.httpMethod ?? "nil","\nHeader :",request.allHTTPHeaderFields ?? "nil","\n---------------------------------")
}
let task = session.dataTask(with: request) { [weak self] (data, response, error) in
var code = 999
var msg = error?.localizedDescription ?? "No Internet Connection"
if let httpresponse = response as? HTTPURLResponse {
code = httpresponse.statusCode
if let data = data,
let dict = try? JSONSerialization.jsonObject(with: data, options: .allowFragments),
let dictionary = dict as? [String:Any]{
if 200 ... 299 ~= code {
if self?.log == .verbose {
print("-------------Response------------\n","URL :",httpresponse.url?.absoluteString ?? "nil","\nMethod :",request.httpMethod ?? "nil","\nHeader :",request.allHTTPHeaderFields ?? "nil"
,"\nStatus :",code,"(\(HTTPURLResponse.localizedString(forStatusCode: code))","\nResult :",dictionary,
"\n---------------------------------")
}
complete?(.success(data: dictionary))
return
}
if self?.log == .error || self?.log == .verbose {
print("-------------Response------------\n","URL :",httpresponse.url?.absoluteString ?? "nil","\nMethod :",request.httpMethod ?? "nil","\nHeader :",request.allHTTPHeaderFields ?? "nil","\nStatus :",code,"(\(HTTPURLResponse.localizedString(forStatusCode: code))","\nResult :",dictionary,
"\n---------------------------------")
}
let er = Network.Error(code: code, description: msg)
complete?(.failed(data:dictionary,error:er))
return
}
msg = "JSONSerialization was failed"
if self?.log == .error || self?.log == .verbose {
print("-------------Response------------\n","URL :",httpresponse.url?.absoluteString ?? "nil","\nMethod :",request.httpMethod ?? "nil","\nHeader :",request.allHTTPHeaderFields ?? "nil","\nStatus :",code,"\(msg)","\nResult : nil",
"\n---------------------------------")
}
let er = Network.Error(code: code, description: msg)
complete?(.failed(data:nil,error:er))
return
}
if self?.log == .error || self?.log == .verbose {
print("-------------Response------------\n","URL :",request.url?.absoluteString ?? "nil","\nMethod :",request.httpMethod ?? "nil","\nHeader :",request.allHTTPHeaderFields ?? "nil","\nStatus :",code,"\(msg)","\nResult : nil",
"\n---------------------------------")
}
let er = Network.Error(code: code, description: msg)
complete?(.failed(data:nil,error:er))
}
return task
}
}
/* Example mocking
class MockCity : NetworkProtocol {
}
*/