You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

TCP Socket应用类HTTP请求响应实现:泛型在completionHandler中的运用

搞定APIClient send方法的泛型Completion Handler问题

嘿,这个场景我熟!咱们直接从协议定义到实现一步步来,完美适配你想要的类HTTP请求响应模式和Socket层抽象。

第一步:完善APIClient协议的方法签名

首先,我们把send方法的泛型约束和completion handler的类型明确下来——用Swift的Result类型来封装成功/失败结果,既符合现代Swift的错误处理习惯,又能完美承载你的泛型ResponseType实例:

protocol APIClient {
    // 把泛型约束<T: ResponseType>直接绑定到send方法上
    func send<T: ResponseType>(
        _ request: SocketMessage, // 这里假设你的请求是SocketMessage类型,可根据实际调整
        completionHandler: @escaping (Result<T, Error>) -> Void
    )
}

这里的核心是让completion handler直接接收Result<T, Error>:成功时拿到解析好的T类型响应,失败时拿到错误信息,语义清晰到不行。

第二步:实现APIClient的send方法

接下来在你的Socket客户端实现类里,处理Socket通信逻辑,收到服务器的SocketMessage后,利用ResponseType的初始化方法生成泛型实例,再通过completion返回:

class ConcreteSocketAPIClient: APIClient {
    private let socket: TCPSocket // 假设你已经有封装好的TCP Socket实例
    
    init(socket: TCPSocket) {
        self.socket = socket
    }
    
    func send<T: ResponseType>(
        _ request: SocketMessage,
        completionHandler: @escaping (Result<T, Error>) -> Void
    ) {
        // 1. 发送请求到服务器
        socket.send(request)
        
        // 2. 监听Socket的响应(这里简化了监听逻辑,实际要根据你的Socket实现调整,比如用回调、Delegate或者Async/Await)
        socket.didReceiveResponse = { frame in
            do {
                // 3. 用ResponseType的init方法解析响应,这里如果初始化可能失败,建议把init改成throws的
                let response = try T.init(with: frame)
                completionHandler(.success(response))
            } catch {
                completionHandler(.failure(error))
            }
        }
    }
}

这里提个优化点:如果你的响应解析可能失败(比如SocketMessage缺少必要字段),建议把ResponseType的初始化方法改成throws的,这样就能用do-catch捕获解析错误,比如:

protocol ResponseType {
    init(with frame: SocketMessage) throws
}

// 对应的MessageAck实现
struct MessageAck: ResponseType {
    var messageId: String
    
    init(with frame: SocketMessage) throws {
        guard let messageId = frame.messageId else {
            throw ParsingError.missingMessageId
        }
        self.messageId = messageId
    }
}

// 自定义解析错误
enum ParsingError: Error, LocalizedError {
    case missingMessageId
    
    var errorDescription: String? {
        switch self {
        case .missingMessageId:
            return "响应缺少必要的messageId字段"
        }
    }
}

第三步:调用send方法的示例

现在调用起来就非常丝滑了,Swift还能自动推断泛型类型,完全不用额外的类型转换:

// 初始化你的Socket客户端
let socket = TCPSocket(host: "your-server.com", port: 1234)
let apiClient = ConcreteSocketAPIClient(socket: socket)

// 构造请求
let request = SocketMessage(messageId: "req-001", content: "Hello Server!")

// 发送请求并处理响应
apiClient.send(request) { result in
    switch result {
    case .success(let ack):
        print("收到服务器确认,消息ID:\(ack.messageId)")
    case .failure(let error):
        print("请求或解析失败:\(error.localizedDescription)")
    }
}

为什么这么做?

  • 泛型直接绑定到send方法,确保每个请求都能对应到正确的响应类型,类型安全拉满
  • Result类型统一了成功/失败的回调方式,避免了传统的可选值+错误参数的混乱写法
  • 把Socket层的解析逻辑封装在ResponseType的初始化方法里,完美实现了Socket层和业务逻辑的分离,符合你想要的抽象需求

内容的提问来源于stack exchange,提问作者seidju

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.22 08:26:58