// // APIClient.swift // import Foundation import Alamofire import ReactiveCocoa import ReactiveSwift /** 통신 구현 객체 - Parameters: - url: String not Optinal 기본주소 - reqParam: dictionary Optinal 전달 파라메터 - headerParam: dictionary Optinal 헤더 파라메터 - fileParam: dictionary Optinal 파일 파라메터 - progressShow: Bool 프로그래스바의 표현여부 - entype: ENCTYPE 인코딩타입, default 기본 json option - ENCTYPE: alamofire의 인코딩 타입을 감추는 래퍼 - callBack: async 호출일 경우 외부 클로저와 브릿징해준다 */ class APIClient: NSObject { private var url:String! private var reqParam:[String:Any]? private var headerParam:[String:String]? private var fileParam:[[String:Data]]? private var progressShow:Bool = true private var entype:ENCTYPE = .default private var returnType:ENCTYPE = .json enum ENCTYPE{ case `default` case json func getParameterEncoding() -> ParameterEncoding { switch self { case .default: return URLEncoding.default case .json: return JSONEncoding.default } } } private var method:METHOD = .post enum METHOD { case get case post func getHTTPMethod() -> HTTPMethod{ switch self { case .get: return HTTPMethod.get case .post: return HTTPMethod.post } } } private var callBack = MutableProperty(nil) /** 초기화 - Parameters: - url: String not Optinal 기본주소 - reqParam: dictionary Optinal 전달 파라메터 - headerParam: dictionary Optinal 헤더 파라메터 - fileParam: dictionary Optinal 파일 파라메터 */ init(_ url:String) { super.init() self.url = url } /** 파라메터설정 - Parameters: - type: 딕셔너리 - Returns: self */ func param(reqParam:[String:Any]) -> APIClient { self.reqParam = reqParam return self } /** 파라메터설정 - Parameters: - type: 딕셔너리 - Returns: self */ func param(fileParam:[[String:Data]]) -> APIClient { self.fileParam = fileParam return self } /** 파라메터설정 - Parameters: - type: 딕셔너리 - Returns: self */ func param(headerParam:[String:String]) -> APIClient { self.headerParam = headerParam return self } /** 인코딩 타입설정 - Parameters: - type: ENCTYPE - Returns: self */ func enType(_ type:ENCTYPE) -> APIClient { self.entype = type return self } /** http 타입설정 - Parameters: - type: METHOD - Returns: self */ func meThod(_ method:METHOD) -> APIClient { self.method = method return self } /** 프로그래스바의 표현여부 - Parameters: - isPro: Bool 표현여부 - Returns: self */ func isProgress(_ isPro:Bool) -> APIClient { self.progressShow = isPro return self } /** 파일 업로드를 위한 펑션 - Parameters: - multipartFormData: closure로 들어오는 upload 객체 */ func upload(multipartFormData:MultipartFormData){ guard let files = self.fileParam else{ return } for file in files{ for (name, data) in file{ multipartFormData.append(data, withName: name, fileName: "temp.jpg", mimeType: self.mimeTypeForData(data:data)) } } guard let params = self.reqParam else{ return } for (key, val) in params{ if let strigVal = val as? String{ multipartFormData.append(strigVal.data(using: .utf8)!, withName: key) }else{ multipartFormData.append(String(describing: val).data(using: .utf8)!, withName: key) } } } /** 콜백 리턴을 넣어주는 펑션 - Parameters: - json: closure로 들어오는 DataResponse 객체 */ func responseJson(json:DataResponse){ self.callBack.value = json.value as? [String:Any] } /** 콜백 리턴을 넣어주는 펑션 - Parameters: - data: closure로 들어오는 DataResponse 객체 */ func responseData(data:DataResponse){ self.callBack.value = data.value } /** 파일업로드 호출 펑션 직접 alamofire를 호출 */ func multiUpload(){ Alamofire.upload(multipartFormData: self.upload, usingThreshold: UInt64(), to: self.url, method: self.method.getHTTPMethod(), headers: self.headerParam, encodingCompletion:{ encodingResult in switch encodingResult { case .success(let upload, _, _): switch self.returnType { case .default: upload.responseData(completionHandler: self.responseData) case .json: upload.responseJSON(completionHandler: self.responseJson) } case .failure(let encodingError): print("error:\(encodingError)") } }) } /** 메인 비동기 호출 펑션 fileParam이 있을경우 self.multiUpload()로 점프한다 그외 인코딩 타입에 따라 리스폰스 받은 객체를 completionHandle로 전송한다. - Parameters: - completionHandle: 클로저를 받아 비동기 호출후 값을 담아 전송 */ typealias CallBackHandler = (T) -> Void public func connect(completionHandle:@escaping CallBackHandler){ if T.self == [String:Any].self { self.returnType = .json }else{ self.returnType = .default } self.callBack.signal.observe { (item) in guard let value = item.value as? T else{ return } completionHandle(value) } guard self.fileParam == nil else { self.multiUpload() return } let api = Alamofire.request(self.url, method: self.method.getHTTPMethod(), parameters: self.reqParam, encoding: self.entype.getParameterEncoding(), headers: self.headerParam) switch self.returnType { case .default: api.responseData(completionHandler: self.responseData) case .json: api.responseJSON(completionHandler: self.responseJson) } } /** 동기화 호출 펑션 - Returns: DataResponse JSON OBJECT를 동기화 코드로 전송한다. */ public func get(type:T) -> DataResponse { if T.self == [String:Any].self { self.returnType = .json }else{ self.returnType = .default } let req = Alamofire.request(self.url, method: self.method.getHTTPMethod(), parameters: self.reqParam, encoding: self.entype.getParameterEncoding(), headers: self.headerParam) let semaphore = DispatchSemaphore(value: 0) var result: DataResponse! var serializer: DataResponseSerializer! switch self.returnType { case .default: serializer = DataRequest.dataResponseSerializer() as? DataResponseSerializer case .json: serializer = DataRequest.jsonResponseSerializer() as? DataResponseSerializer } req.response(queue: DispatchQueue.global(qos: .default), responseSerializer: serializer) { response in result = response as DataResponse semaphore.signal() } _ = semaphore.wait(timeout: DispatchTime.distantFuture) return result } /** 파일 타입을 알아내는 펑션 - Parameters: - data: Data file 객체 - Returns: String mime 타일을 반납한다. ex) image/jpeg */ public func mimeTypeForData(data:Data) -> String{ var c = [UInt32](repeating: 0, count: 1) (data as NSData).getBytes(&c, length: 1) switch (c[0]) { case 0xFF: return "image/jpeg" case 0x89: return "image/png" case 0x47: return "image/gif" case 0x49: return "image/tiff" case 0x4D: return "image/tiff" case 0x25: return "application/pdf" case 0xD0: return "application/vnd" case 0x46: return "text/plain" default: return "application/octet-stream" } } }