// // Service.swift // Juick // // Created by Vitaly Takmazov on 16.09.2020. // Copyright © 2020 com.juick. All rights reserved. // import Foundation class Service : NSObject, URLSessionDelegate, URLSessionTaskDelegate { let backgroundQueue = OperationQueue() let baseURL : URL? lazy var authorizationHeader : String? = { if let token = UserDefaults.standard.string(forKey: "token") { return "Juick \(token)" } return nil; }() override init() { let baseURLString = Bundle.main.object(forInfoDictionaryKey:"base_url") as! String debugPrint("Initializing with \(baseURLString) base URL") self.baseURL = URL(string: baseURLString) super.init() } func url(url:URL, params:[String: String]) -> URL { let parametersUrl = NSURLComponents(url: url, resolvingAgainstBaseURL: true) var items : [URLQueryItem] = [] for (key, value) in params { items.append(URLQueryItem(name: key, value: value)) } parametersUrl?.queryItems = items return (parametersUrl?.url!)! } func fetchData(url:URL, postData:Data?, boundary:String?, header:String?, callback: @escaping (Data?, Error?) -> Void) { var request = URLRequest(url: url) if (header != nil) { request.addValue(header!, forHTTPHeaderField:"Authorization") } if (boundary != nil) { request.httpMethod = "POST"; request.httpBody = postData; let contentType = "multipart/form-data; boundary=\(boundary!)" request.setValue(contentType, forHTTPHeaderField:"Content-Type") } else if (postData != nil) { request.httpMethod = "PUT"; request.httpBody = postData; request.setValue("application/json", forHTTPHeaderField:"Content-Type") } else { request.httpMethod = "GET"; } let task = URLSession.shared.dataTask(with: request, completionHandler: { (data, response, error) in if (error != nil) { OperationQueue.main.addOperation { callback(nil, error) } } else { let statusCode = (response as? HTTPURLResponse)?.statusCode ?? 457 if (response == nil || statusCode != 200) { let url = request.url let err = NSError(domain: "JuickErrorDomain", code: statusCode, userInfo: ["url":url!.absoluteString]) OperationQueue.main.addOperation { callback(nil, err) } } else { OperationQueue.main.addOperation { callback(data, error) } } } }) self.backgroundQueue.addOperation { task.resume() } } private func multipartData(params:[String:String], boundary:String) -> Data { var body = Data() for (key, value) in params { body.append("--\(boundary)\r\n".data(using: .utf8)!) body.append("Content-Disposition: form-data; name=\(key)\r\n\r\n".data(using: .utf8)!) body.append("\(value)\r\n".data(using: .utf8)!) } body.append("--\(boundary)--".data(using: .utf8)!) return body; } private func get(path:String, params:[String:String], callback:@escaping (NSDictionary?, Error?) -> Void) { if let url = URL(string: path, relativeTo: self.baseURL) { let requestUrl = params.isEmpty ? url : self.url(url: url, params: params) fetchData(url: requestUrl, postData: nil, boundary: nil, header: self.authorizationHeader) { (responseData, err) in if (err != nil) { callback(nil, err) } else { if let data = responseData, let jsonData = try? JSONSerialization.jsonObject(with: data, options: []) { callback(jsonData as? NSDictionary, nil) } else { callback(nil, NSError(domain: "JuickErrorDomain", code: 500, userInfo: ["message":"JSON error"])) } } } } } func post(path: String, params:[String:String], callback:@escaping (NSDictionary?, Error?) -> Void) { if let url = URL(string: path, relativeTo: self.baseURL) { let boundary = "Boundary-\(UUID().uuidString)"; fetchData(url: url, postData:multipartData(params: params, boundary: boundary), boundary: boundary, header: self.authorizationHeader) { (responseData, err) in if (err != nil) { callback(nil, err) } else { if let data = responseData, let jsonData = try? JSONSerialization.jsonObject(with: data, options: []) { callback(jsonData as? NSDictionary, nil) } else { callback(nil, NSError(domain: "JuickErrorDomain", code: 500, userInfo: ["message":"JSON error"])) } } } } } func fetchImage(url:URL, callback:@escaping(Data?)-> Void) { fetchData(url: url, postData: nil, boundary: nil, header: self.authorizationHeader) { (data, err) in if (err != nil) { callback(nil); } else { callback(data); } } } }