apple/Docs, iOS, Swift

[Moya] Unable to parse empty data 대응하기

lgvv 2022. 9. 29. 17:38

Unable to parse empty data 대응하기

 

 

Moya 연습을 위해 사용한 사이트: https://reqres.in/

 

Reqres - A hosted REST-API ready to respond to your AJAX requests

Native JavaScript If you've already got your own application entities, ie. "products", you can send them in the endpoint URL, like so: var xhr = new XMLHttpRequest(); xhr.open("GET", "https://reqres.in/api/products/3", true); xhr.onload = function(){ conso

reqres.in

 

 

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

 

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

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

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

 

 

✅ 전체 코드 ✅

import Foundation

import Moya
import RxCocoa
import RxSwift
import RxMoya

// MARK: - GWNetworkService

public protocol GWNetworkServable {
    func request<API>(_ api: API) -> Single<API.Response> where API: GWServiceAPI
}

class GWNetworkService: GWNetworkServable {
    public init() { }
    
    public func request<API>(_ api: API) -> Single<API.Response> where API: GWServiceAPI {
        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(GWDTO.Empty() as! API.Response)
                    }
                    print("error: ", error)
                    
                    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 = GWCustomProvider<MultiTarget>()
}

// MARK: - GWServiceAPI

import Foundation
import Moya

public protocol GWServiceAPI: TargetType {
    associatedtype Response: Decodable
}

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

// MARK: - GWCustomProvider

import Foundation

import Moya

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


public enum GWDTO { 
	public struct Empty: Decodable { }
}

public struct GWDeleteAPI: GWServiceAPI {
    public typealias Response = GWDTO.Empty // ✅ 이게 핵심
    
    public var path: String { "/api/users/2" }
    public var method: Moya.Method { .delete }
    public var task: Moya.Task { .requestPlain }
}

 

 

초록색으로 핵심이라고 둔 부분을 보면 Response를 Delete임에도 Empty로 지정하여 처리했다.