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
相关产品推荐
相关产品推荐

