Search code examples
swiftrx-swiftswifty-jsoncodablemoya

handling json response Observable swift


I have an application which uses SwiftyJSON and works. How ever, I now want to expand the project and refactor the codes but I am having a bit of issue as I am now switching to Codable and I need to be able to mapJSON from any path and not a hard coded path. Currently my jsonResponse looks like this

/// handle the network response and map to JSON
    /// - returns: Observable<JSON>
    func handleResponseMapJSON() -> Observable<Result<JSON, ORMError>> {

        return self.map { representor in

            guard let response = representor as? Moya.Response else {
                return .failure(ORMError.ORMNoRepresentor)
            }

            guard ((200...299) ~= response.statusCode) else {
                return .failure(ORMError.ORMNotSuccessfulHTTP)
            }

            guard let json = JSON.init(rawValue: response.data),
                json != JSON.null,
                let code = json["code"].int else {
                    return .failure(ORMError.ORMParseJSONError)
            }

            guard code == BizStatus.BizSuccess.rawValue else {
                let message: String = {
                    let json = JSON.init(data: response.data)
                    guard let msg = json["status"].string else { return "" }
                    return msg
                }()
                log(message, .error)
                return .failure(ORMError.ORMBizError(resultCode: "\(code)", resultMsg: message))
            }

            return .success(json["result"])

        }
    }

how do I eliminate the passage of hardcoded json[""] value. Any help is appreciated


Solution

  • I suggest you try something like this:

    protocol ResponseType: Codable {
        associatedtype ResultType
        var status: String { get }
        var code: Int { get }
        var result: ResultType { get }
    }
    
    func handleResponseMap<T, U>(for type: U.Type) -> (Any) -> Result<T, ORMError> where U: ResponseType, T == U.ResultType {
        return { representor in
            guard let response = representor as? Moya.Response else {
                return .failure(.ORMNoRepresentor)
            }
            guard ((200...299) ~= response.statusCode) else {
                return .failure(.ORMNotSuccessfulHTTP)
            }
            return Result {
                try JSONDecoder().decode(U.self, from: response.data)
                }
                .mapError { _ in ORMError.ORMParseJSONError }
                .flatMap { (response) -> Result<T, ORMError> in
                    guard response.code == BizStatus.BizSuccess.rawValue else {
                        log(response.status, .error)
                        return Result.failure(ORMError.ORMBizError(resultCode: "\(response.code)", resultMsg: response.status))
                    }
                    return Result.success(response.result)
            }
        }
    }
    

    Then you can map directly to your Codable type:

    let result = self.map(handleResponseMap(for: MyResponse.self))
    

    In the above, result will end up being an Observable<Result<ResultType, ORMError>>