Search code examples
swiftrx-swiftmoya

Casting Moya Response error to defined type


I am using RxMoya for my networking calls and extending PremitiveSequence and Response so as to handle the error coming back. I declared a struct of Networking error which I could use to get all the error details and as such Pass the error message via the BaseResponse Model. Here is my NetwokingError struct

public struct NetworkingError: Error {
    let httpResponse: HTTPURLResponse?
    let networkData: Data?
    let baseError: Error
}

For my coding, I have extended the primitive sequence as follows

public extension PrimitiveSequence where TraitType == SingleTrait,

ElementType == Response {

    func mapObject<T: Codable>(_ type: T.Type, path: String? = nil) -> Single<T> {
        return flatMap { response -> Single<T> in
            return Single.just(try response.mapObject(type, path: path))
        }
    }

    func mapArray<T: Codable>(_ type: T.Type, path: String? = nil) -> Single<[T]> {
        return flatMap { response -> Single<[T]> in
            return Single.just(try response.mapArray(type, path: path))
        }
    }

    func filterSuccess() -> Single<E> {
        return flatMap { (response) -> Single<E> in
            if 200 ... 299 ~= response.statusCode {
                return Single.just(response)
            }

            print("THIS ERROR JSON jsonObject2 xx mm \(response.data)")

            do {
                let jsonObject2 = try JSONSerialization.jsonObject(with: response.getJsonData(), options: .allowFragments)
                print("THIS ERROR JSON jsonObject2 xx \(jsonObject2)")
                let jsonObject = try JSONSerialization.jsonObject(with: response.getJsonData(), options: .allowFragments) as? NetworkingError

                print("THIS ERROR JSON  xx \(jsonObject)")
                return Single.error(jsonObject ?? NetworkingError.self as! Error)
            }
        }
    }
}

if I run this code here, The app crashes return Single.error(jsonObject ?? NetworkingError.self as! Error)

in my code, I am passing data like

func postVerifyApp(challenge: Int, identifier: String) -> Observable<AuthResponse> {

        return provider.rx.request(.postVerifyApp(challenge: challenge, identifier: identifier))

            .filterSuccess()
            .mapObject(AuthResponse.self)
            .asObservable()
            .flatMap({ authResponse -> Observable<AuthResponse> in
                return self.sendTokenToServer(authResponse)
            })
    }

then I am working with this in my presenter class like this

func postVerifyApp(challenge: Int, identifier: String) {
        view?.setProgress(enabled: true)
        source.postVerifyApp(challenge: challenge, identifier: identifier)
            .retry(.delayed(maxCount: 2, time: 2.5), shouldRetry: networkRetryPredicate)
            .asSingle()
            .subscribe(onSuccess: { [weak self] response in
                guard let presenter = self, let view = presenter.view else {return}
                view.setProgress(enabled: false)
                log(response, .json)
                guard let data = response.data else {
                    return }

                view.showVerifySuccess()
                }, onError: { [weak self] error in
                    guard let presenter = self, let view = presenter.view else {return}
                    print("MESSAGE X \(error.localizedDescription)")
                    if let error = error as? NetworkingError {
                        print("MESSAGE X httpResponse \(error.httpResponse)")
                    }
                    view.setProgress(enabled: false)

            }).disposed(by: disposeBag)
    }

I want to be able to pass this Error and extract the error message and passing it to the console.

This is what my base Model looks like

struct ResponseBase<T: Codable>: Codable {
    var error: Bool?
    var message: String?
    var data: T

    var isSucessful: Bool {
        return error == false
    }

}

Solution

  • The expression used to construct the Single.error can not cast as Error. Firstly, you are trying to cast a jsonObject (a Dictionary) as Error. On the right hand, on the ifNull expression, you are trying to cast a metatype (Networking.Type) as an Error.

    To solve your casting problem you can use this modified NetworkingError.

    public struct NetworkingError: Error {
    
        let httpResponse: HTTPURLResponse?
        let networkData: Data?
        let baseError: MoyaError
    
        init(_ response:Response) {
            self.baseError = MoyaError.statusCode(response)
            self.httpResponse = response.response
            self.networkData = response.data
        }
    
        func getLocalizedDescription() -> String {
    
           return self.baseError.localizedDescription
        }
    }
    

    Having this, modify the closure in the filterSuccess to create the NetworkingError object, passing it the Response, just like this:

    func filterSuccess() -> Single<E> {
        return flatMap {
            (response) -> Single<E> in
            if 200 ... 299 ~= response.statusCode {
               return Single.just(response)
            } else {
               let netError = NetworkingError(response)
               return Single.error(netError)
            }
        }
    }
    

    I encourage you to take a look at the MoyaError definition