apple/iOS, UIKit, Documentation

Moya Unable to parse empty data 대응하기

lgvv 2022. 9. 29. 17:38

Moya Unable to parse empty data 대응하기

 

 

Moya 연습을 위해 사용한 사이트

https://reqres.in/

 

https://reqres.in/api/users/2

 


문제상황

Moya를 사용하면서 objectMapping 시에, 필드값 자체가 내려오지 않으면 정상적으로 처리되지 않는 문제가 있음.

구글에 검색을 많이 해봤는데, 대부분이 Empty인 상황을 Error로 정의하고 처리하고 있는 것을 확인.

다만, statusCode가 200번대라서 Error로 정의하는 것을 별로 좋지 못하다고 판단하여 다른 방식으로 처리해보고자 함.

 

 

전체 코드

아래는 샘플 코드

  • 샘플 코드의 아래에 핵심 포인트를 주석으로 표기

import Moya
import RxMoya
import RxCocoa
import RxSwift
import Foundation

protocol NetworkServable {
    func request<API>(_ api: API) -> Single<API.Response>
}

class GWNetworkService: NetworkServable {
    public init() { }
    
    public func request<API>(_ api: API) -> Single<API.Response> {
        let endPoint = MultiTarget(api)

        return provider.rx.request(endPoint)
            .flatMap { response ->
                Single<API.Response> in
                
                do {
                    try self.httpProcess(response: response)
                    return .just(try response.map(API.Response.self))
                    
                } catch NetworkError.httpStatus(let statusCode) {
                    return .error(NetworkError.httpStatus(statusCode))
                    
                } catch MoyaError.objectMapping(let error, let response) {
                    if (200..<299) ~= response.statusCode {
                        return .just(ResponseDTO.Empty() as! API.Response)
                    }
                    return .error(error)
                }
            }
    }

    private func httpProcess(response: Response) throws {
        guard 200...299 ~= response.statusCode else {
            throw NetworkError.httpStatus(response.statusCode)
        }
    }

    enum NetworkError: Error {
        case httpStatus(Int)
    }
    
    let provider = APIProvider<MultiTarget>()
}

protocol ServiceAPI: TargetType {
    associatedtype Response: Decodable
}

extension ServiceAPI {
    var baseURL: URL { return URL(string: "https://reqres.in")! }
    var headers: [String : String]? { return nil }
}

class APIProvider<Target: TargetType>: MoyaProvider<Target> {
    init() {
        super.init()
    }
}

enum ResponseDTO {
    struct Empty: Decodable { }
}

struct DeleteAPI: ServiceAPI {
    // !!!: - ✅ 이게 핵심
    typealias Response = ResponseDTO.Empty
    
    var path: String { "/api/users/2" }
    var method: Moya.Method { .delete }
    var task: Moya.Task { .requestPlain }
}