The backend I'm dealing with is sending custom error code and message in response data in certain situations.
Although it's not the perfectly designed backend, there is no way to change it right now.
The goal is to transform this response data to a Moya.Error.underlyingError(NSError). This NSError should have a custom domain and the values sent from the server as its userInfo.
Main reason for that, I'm working on a framework which will be used in an Objective-C project and this Moya integrated framework should return values similar to old Objective-C implementation that the project is using.
I didn't want to do filtering after each call to request(:) of my providers, so I want to do this in a common place.
Example response:
Http status code: 401
Response data:
{errorCode: 3, errorMessage: "Authorization required"}
Current solution:
extension ReactiveCocoaMoyaProvider {
func customRequest(token: TargetType) -> SignalProducer<Response, Moya.Error> {
return self.request(token: token).flatMap(.latest) { (response: Response) -> SignalProducer<Response, Moya.Error> in
let json = JSON(data: response.data)
// Convert response.data to APIError instance
guard let apiError = APIError(jsonData: json) else {
return SignalProducer<Response, Moya.Error>(value: response)
}
let userInfo = [NSLocalizedDescriptionKey: apiError.message, "httpStatus": response.statusCode, "areaStatus": apiError.errorCode, "url": response.request?.url?.absoluteString] as [String : Any]
let mappedError: NSError = NSError(domain: "custom error domain", code: response.statusCode, userInfo: userInfo)
return SignalProducer<Response, Moya.Error>(error: .underlying(mappedError))
}
}
}
Then, user of my framework needs to call customRequest(:) for each provider call, which is not really nice. Another problem is I can't write a stub for the extension method to test it.
Nicer solution would be subclassing ReactiveCocoaProvider and overriding request(:) method, butrequest(:)is not defined as open (not like inMoyaProvider`).
So is it possible to define ReactiveCocoaProvider.request(:) as open?
In fact, both of these solutions don't seem much convenient. Is there a better solution?
My suggestion would be letting Plugins alter the result by returning the altered result from didReceiveResponse() protocol method.
However, there can be multiple protocols which could make this solution complicated. Maybe a single instance plugin with different kind can be used, something like "ResultTransformerPlugin".
@cocoatoucher Have you find out the solution?
Now you can create your own custom Plugin, may be named as MyErrorPlugin. Then you can parse your response data and change it to Error if need:
public struct MyErrorPlugin: PluginType {
func process(_ result: Result<Moya.Response, MoyaError>, target: TargetType) -> Result<Moya.Response, MoyaError> {
let json = JSON(data: response.data)
// Convert response.data to APIError instance
guard let apiError = APIError(jsonData: json) else {
return Result.success(response.data).
}
let userInfo = [NSLocalizedDescriptionKey: apiError.message, "httpStatus": response.statusCode, "areaStatus": apiError.errorCode, "url": response.request?.url?.absoluteString] as [String : Any]
let mappedError: NSError = NSError(domain: "custom error domain", code: response.statusCode, userInfo: userInfo)
return Result.failure(mappedError)
}
}
So, you don't need to subclass ReactiveCocoaMoyaProvider
This issue has been marked as stale because it has not had recent activity. It will be closed if no further activity occurs.
This issue has been auto-closed because there hasn't been any activity for 59 days. However, we really appreciate your contribution, so thank you for that! 馃檹 Also, feel free to open a new issue if you still experience this problem 馃憤.
Most helpful comment
@cocoatoucher Have you find out the solution?
Now you can create your own custom Plugin, may be named as
MyErrorPlugin. Then you can parse your response data and change it to Error if need:So, you don't need to subclass
ReactiveCocoaMoyaProvider