如何为Swift Protobuf生成的Message结构体添加通用Codable扩展?
解决方案
1. 利用Swift Protobuf公共协议做条件扩展
Swift Protobuf生成的所有消息类型都遵循SwiftProtobuf.Message公共协议,我们可以基于这个协议编写条件扩展,为所有符合条件的类型提供Codable的默认实现,彻底避免重复编写样板代码:
import SwiftProtobuf // 给所有遵循SwiftProtobuf.Message且同时遵循Codable的类型,提供Codable默认实现 extension SwiftProtobuf.Message where Self: Codable { public init(from decoder: Decoder) throws { let container = try decoder.singleValueContainer() let data = try container.decode(Data.self) self = try Self(serializedData: data) } public func encode(to encoder: Encoder) throws { let data = try self.serializedData() var container = encoder.singleValueContainer() try container.encode(data) } }
之后,你只需要给每个生成的消息类型添加一行声明即可,无需重复编写实现:
extension Message1: Codable {} extension Message2: Codable {}
2. 用标记协议简化声明(可选)
如果觉得每个类型加一行还是繁琐,可以定义一个标记协议整合Codable和SwiftProtobuf.Message,再给这个协议提供默认实现:
import SwiftProtobuf // 定义标记协议 protocol ProtobufCodable: Codable, SwiftProtobuf.Message {} // 给标记协议提供Codable默认实现 extension ProtobufCodable { public init(from decoder: Decoder) throws { let container = try decoder.singleValueContainer() let data = try container.decode(Data.self) self = try Self(serializedData: data) } public func encode(to encoder: Encoder) throws { let data = try self.serializedData() var container = encoder.singleValueContainer() try container.encode(data) } }
然后给每个消息类型添加标记协议的遵循声明:
extension Message1: ProtobufCodable {} extension Message2: ProtobufCodable {}
3. 进阶:用代码生成工具完全自动化
如果项目中有大量Protobuf消息类型,手动添加扩展声明也麻烦,可以用Sourcery这类代码生成工具自动完成:
- 编写Sourcery模板文件(比如
ProtobufCodable.stencil):
{% for type in types.based.SwiftProtobuf.Message %} extension {{ type.name }}: Codable {} {% endfor %}
- 配置Sourcery扫描你的Protobuf生成代码目录,它会自动为所有
SwiftProtobuf.Message类型生成Codable扩展声明,结合第一步的条件扩展,实现完全无样板代码。
关于泛型扩展的说明
Swift不允许直接通过泛型扩展让类型遵循新协议(比如extension<T: SwiftProtobuf.Message> T: Codable这种语法是非法的),但我们可以通过条件协议扩展(即上面用到的where Self: Codable)来为符合条件的类型提供默认实现,再通过简单的扩展声明让类型正式遵循Codable,这是当前最简洁的方案。
内容的提问来源于stack exchange,提问作者Gary Rudolph
相关产品推荐
相关产品推荐

