본문 바로가기

Swift 공부

RxMoya를 직접 만들어서 사용해보자

//Moya 처럼 사용 가능하게 만들기위해
typealias HTTPHeaders = [String : String]

protocol TargetType {
    var baseURL: String { get }
    var headers : HTTPHeaders { get }
    var method: HTTPMethod { get }
    var path: String { get }
    var parameters: RequestParams { get }
}

enum HTTPMethod : String{
    case GET = "GET"
    case POST = "POST"
}

//요청은 Json으로 만들기 때문에 Encodeable
enum RequestParams {
    case query(_ parameter: Encodable?)
    case body(_ parameter: Encodable?)
}

 

Moya에서 사용하던것과 똑같게 만들기위해 필요한 파라미터들을 protocol로 만들어 줍니다 다양한 method가 필요하다면

추가해주시면됩니다.

 

그럼 프로토콜로 만들어준것을 urlrequest로 만들기위해  asURLRequest() 함수를 만들어 줍니다.

 

extension TargetType {
    
    // URLRequestConvertible 구현
    func asURLRequest() throws -> URLRequest {
        guard let url = URL(string: baseURL)?.appendingPathComponent(path) else {
            throw AsURLRequestError.URLError
        }
        
        
        
        var urlRequest = URLRequest(url: url)
        urlRequest.httpMethod = method.rawValue
        
        for header in headers {
            urlRequest.addValue(header.value, forHTTPHeaderField: header.key)
        }
        
        
        switch parameters {
        case .query(let request):
            let params = request?.toDictionary ?? [:]
            let queryParams = params.map { URLQueryItem(name: $0.key, value: "\($0.value)") }
            var components = URLComponents(string: url.appendingPathComponent(path).absoluteString)
            components?.queryItems = queryParams
            urlRequest.url = components?.url
        case .body(let request):
            let params = request?.toDictionary ?? [:]
            urlRequest.httpBody = try JSONSerialization.data(withJSONObject: params, options: [])
        }
        
        
        return urlRequest
    }
}

enum AsURLRequestError : Error{
    
    case URLError
    
}

 

지난번 포스팅에서 말한것처럼 throw를 이용해 에러를 핸들링을 해준다 지금은 단순히 URL을 파싱하지 못하였을때 생기는 오류만을 가정하여 Error을 만들었는데 필요에따라 추가적으로 Error를 만들어 사용해주면된다.

 

 

사용방법은 간단하다

 

Moya를 사용하듯이 enum의 API 의 리스트들을 정의해주고

TargetType 을 채택하여 기본값을 셋팅해준다.

 

enum AppStoreApi {
    case SERARCH_RECOMEND_APP(term : String)
}

extension AppStoreApi : TargetType{
    
    
    var baseURL: String {
        return  "https://itunes.apple.com"
    }
    
    var headers : HTTPHeaders {
        return [:]
    }
    
    var method: HTTPMethod {
        return .GET
    }
    
    var path: String {
        switch self {
        case .SERARCH_RECOMEND_APP :
            return "/search"
        }
        
        
    }
    var parameters: RequestParams {
        switch self{
        case .SERARCH_RECOMEND_APP(let term) :
            let param : SearchRequestModel = SearchRequestModel(term : term)
            
            return .query(param)
            
        }
    }
    
}

 

 

실제로 데이터를 통신을위해 사용하는 코드

class TempAPI : NetworkServiceProtocol{
    static let shared = TempAPI()
    let jsonDecoder = JSONDecoder()
    
    func fetchRepositories<T: Decodable>(type : T.Type , _ api: AppStoreApi) throws ->  Observable<T> {
        return URLSession.shared.rx
            .data(request : try api.asURLRequest())
            .map{ [weak self] item in
                guard let self = self else { throw SelfError.NonSelf }
                let product = try self.jsonDecoder.decode(T.self, from: item)
                return product
            }
    }
}

enum SelfError : Error{
    
    case NonSelf
    
}

 

URLSession을 통해 데이터를 요청하고 함수를 제네릭하게 만들어 리턴시킬 데이터형태를 선언을 받는다.

URLSession.shared.rx 를 통해 비동기를 Rx로 리턴을 해준다. 

그리고 마찬가지로 throws 를 통해 생기는 에러들을 컨트롤 해준다.

 

viewDidLoad
            .flatMap { [weak self]  _ -> Observable<RecomendSearchResponse> in
                guard let self = self else { return .never() }
                return try self.networkAPI.fetchRepositories(type: RecomendSearchResponse.self, .SERARCH_RECOMEND_APP(term: "game"))
                    .catch{ error in
                        return .never()
                    }
            }
            .map{ $0.results }

 

실제 사용은 이런식으로 진행해주면된다.  type에 리턴받을 데이터의 형태를 적어주고 그 뒤에 내가 사용할 API와 변수를 넘겨준다.

그리고 catch문을 통해 error을 핸들링을 진행해주면된다.

 

 

지금 예제로 만들어진 함수는 retry , 여러 네트워크 오류에대한 error handling등이 이뤄지지 않고 최소한의 예외처리만을 진행하여 만들어진 예제다 retry , error handling 을 Alamofire와 , Moya를 사용한다면 상당히 쉽게 처리할수있다 하지만 라이브러리를 사용하기전 기본적인 원리 , 구조를 이해하고 진행해야하기에 직접만들어서 통신까지 진행해보았고 자료를 공유한다.

 

 

'Swift 공부' 카테고리의 다른 글

RxSwift Disposable 이란?  (0) 2022.04.13
Property Wrapper  (0) 2022.04.12
RxDataSources Multiple Object Multiple Section  (1) 2022.04.08
Swift Throw Error Handling  (0) 2022.04.07
Async / await의 도입 스위프트 5.5  (0) 2022.04.05