如何在 Swift 中使用带有 Result 的平面图

Posted

技术标签:

【中文标题】如何在 Swift 中使用带有 Result 的平面图【英文标题】:How to use flatmap with Result in Swift 【发布时间】:2021-05-18 18:48:37 【问题描述】:

所以,我有这个从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)
    

端点

// 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 () )
    

let result = response.result.flatMap(endpoint.decode) xCode 正在抛出

Type of expression is ambiguous without more context

response.result 的类型是Result&lt;Data, AFError&gt;

我想将其平面映射到 Result&lt; Response, AFError&gt;

我试过了

let result = response.result.flatMap  (data) -> Result<Response, AFError> in
 // don't know what to put here

【问题讨论】:

我刚刚尝试了Alamofire v4.9.1 的要点,但在request(_:) 中没有编译错误 我使用的是 5.2.0 然后我得到No type named 'SessionManager' in module 'Alamofire'。在您的问题中提供此类信息可能会很有用 抱歉,我已经解决了所有问题。这只是一张平面地图,我认为这并不重要。 您解决了所有问题,但您仍然提供了要点的链接。你怎么能确定人们会像你一样解决问题?恕我直言,您没有提供minimal, reproducible, example 【参考方案1】:

flatMap(_:) 不接受抛出闭包,而在EndPoint 中,解码是一个抛出闭包:

let decode: (Data) throws -> Response

尝试捕捉错误:

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()
        
    


注意

由于您似乎只对AFError 感兴趣,因此此代码将调用fatalError,但这可能不是一个好主意。

【讨论】:

【参考方案2】:

当你应该使用map时,你正在使用flatMap...

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  

【讨论】:

以上是关于如何在 Swift 中使用带有 Result 的平面图的主要内容,如果未能解决你的问题,请参考以下文章

如何在 swift setMethodCallHandler 中使用传递的参数 - self?.methodName(result: result)

如何避免在带有 Swift 4 的 iOS 11 中使用带有刷新控件的 @objc?

如何在swift中使用带有completionHandler的UIImageView load()函数

如何在 Swift 中使用带有滚动视图的自动布局?

如何在带有 SwiftPM 的 Objective-C 模块中使用 Swift 模块?

如何在 Swift 中使用 Alamofire 上传带有 JSON 参数的图像?