1

So, I have this function which is copied from this GitHub gist.

protocol ClientProtocol {
    func request<Response: Codable>(_ endpoint: Endpoint<Response>)-> Single<Response>
}

final class Client: ClientProtocol {
    
    private let manager: Alamofire.Session
    private let baseURL = URL(string: "http://192.168.20:8080")!
    private let queue = DispatchQueue(label: "<your_queue_label>")
    
    init(accessToken: String) {
        let configuration = URLSessionConfiguration.default
        configuration.headers.add(name: "Authorization", value: "Bearer \(accessToken)")
        configuration.timeoutIntervalForRequest = 15
        self.manager = Alamofire.Session.init(configuration: configuration)
        //self.manager.retrier = OAuth2Retrier()
    }
    
    func request<Response>(_ endpoint: Endpoint<Response>) -> Single<Response> {
        return Single<Response>.create { observer in
            let request = self.manager.request(
                self.url(path: endpoint.path),
                method: httpMethod(from: endpoint.method),
                parameters: endpoint.parameters
            )
            request
                .validate()
                .responseData(queue: self.queue) { response in
                    let result: Result<Response, AFError> = response.result.flatMap(endpoint.decode)
                    switch result {
                        case let .success(val): observer(.success(val))
                        case let .failure(err): observer(.error(err))
                    }
                }
            return Disposables.create {
                request.cancel()
            }
        }
    }
    
    private func url(path: Path) -> URL {
        return baseURL.appendingPathComponent(path)
    }
}

private func httpMethod(from method: Method) -> Alamofire.HTTPMethod {
    switch method {
        case .get: return .get
        case .post: return .post
        case .put: return .put
        case .patch: return .patch
        case .delete: return .delete
    }
}


private class OAuth2Retrier: Alamofire.RequestRetrier {
    func retry(_ request: Request, for session: Session, dueTo error: Error, completion: @escaping (RetryResult) -> Void) {
        if (error as? AFError)?.responseCode == 401 {
            // TODO: implement your Auth2 refresh flow
            // See https://github.com/Alamofire/Alamofire#adapting-and-retrying-requests
        }
        // completion(false, 0)
    }
}

Endpoint

// MARK: Defines
typealias Parameters = [String: Any]
typealias Path = String

enum Method {
    case get, post, put, patch, delete
}


// MARK: Endpoint
final class Endpoint<Response> {
    
    let method: Method
    let path: Path
    let parameters: Parameters?
    let decode: (Data) throws -> Response
    
    init(method: Method = .get, path: Path, parameters: Parameters? = nil, decode: @escaping (Data) throws -> Response) {
        self.method = method
        self.path = path
        self.parameters = parameters
        self.decode = decode
    }
}


// MARK: Convenience
extension Endpoint where Response: Swift.Decodable {
    convenience init(method: Method = .get,  path: Path,  parameters: Parameters? = nil) {
        self.init(method: method, path: path, parameters: parameters) {
            try JSONDecoder().decode(Response.self, from: $0)
        }
    }
}


extension Endpoint where Response == Void {
    convenience init(method: Method = .get, path: Path, parameters: Parameters? = nil) {
        self.init(method: method, path: path, parameters: parameters, decode: { _ in () })
    }
}

At let result = response.result.flatMap(endpoint.decode) xCode is throwing

Type of expression is ambiguous without more context

The type of the response.result is Result<Data, AFError>

I want to flatmap it to Result< Response, AFError>.

I tried

let result = response.result.flatMap { (data) -> Result<Response, AFError> in
 // don't know what to put here
}
OhhhThatVarun
  • 3,981
  • 2
  • 26
  • 49

2 Answers2

1

flatMap(_:) doesn't take throwing closure and in EndPoint, decode is a throwing closure :

let decode: (Data) throws -> Response

try catching the error:

func request<Response>(_ endpoint: Endpoint<Response>) -> Single<Response> {
    return Single<Response>.create { observer in
        let request = self.manager.request(
            self.url(path: endpoint.path),
            method: httpMethod(from: endpoint.method),
            parameters: endpoint.parameters
        )
        request
            .validate()
            .responseData(queue: self.queue) { response in
                let result: Result<Response, AFError> = response.result.flatMap {
                    do {
                        return Result<Response, AFError>.success(try endpoint.decode($0))
                    } catch let error as AFError {
                        return Result<Response, AFError>.failure(error)
                    } catch {
                        fatalError(error.localizedDescription)
                    }
                }
                switch result {
                    case let .success(val): observer(.success(val))
                    case let .failure(err): observer(.failure(err))
                }
            }
        return Disposables.create {
            request.cancel()
        }
    }
}

Note

Since you seem only interested in AFError this code will call fatalError this may not a good idea though.

AnderCover
  • 2,488
  • 3
  • 23
  • 42
1

You are using flatMap when you should be using map...

func example(response: Response, endpoint: Endpoint<Thing>) {
    let result = response.result.map(endpoint.decode)
}

struct Response {
    let result: Result<Data, Error>
}

struct Endpoint<T> {
    func decode(_ data: Data) -> T { fatalError() }
}

struct Thing { }
Daniel T.
  • 32,821
  • 6
  • 50
  • 72