如何定义支持任意Codable类型变量的Swift GraphQL查询结构体?
如何定义支持任意Codable类型变量的Swift GraphQL查询结构体?
我完全懂你这个需求——不同的GraphQL突变或查询需要的变量结构千差万别,总不能为每种变量都写一个单独的Payload结构体,太麻烦了。下面给你两种实用的解决方案,你可以根据自己的代码场景来选:
方案一:使用存在类型 any Codable(灵活适配多种变量类型)
这就是你自己尝试的思路,直接把variables的类型设为any Codable?,这样它就能接受任何符合Codable协议的结构体作为变量,完美适配不同的GraphQL操作需求:
struct Payload: Codable { var query: String var variables: any Codable? // 用存在类型接收任意Codable变量,设为可选支持无变量的查询 } // 你定义的各种变量结构体保持不变 struct Patch: Codable { var id: String // 这里假设ID是String类型,你可以换成项目里实际的ID类型 var patch: [String: String] } struct Vars1: Codable { var id: String var name: String }
使用示例
不管是传Patch还是Vars1,甚至是基础类型(比如[String: String]字典)都没问题:
// 用Patch作为变量创建Payload let patchData = Patch(id: "user_1001", patch: ["email": "new@example.com"]) let patchMutation = "mutation ($id: ID!, $patch: ObjectPatch) { updateUser(id: $id, patch: $patch) { id email } }" let patchPayload = Payload(query: patchMutation, variables: patchData) // 用Vars1作为变量创建Payload let nameVars = Vars1(id: "post_2001", name: "New Post Title") let nameMutation = "mutation ($id: ID!, $name: String!) { updatePost(id: $id, name: $name) { id name } }" let namePayload = Payload(query: nameMutation, variables: nameVars) // 无变量的查询也支持 let noVarQuery = "query { getAllUsers { id name } }" let noVarPayload = Payload(query: noVarQuery, variables: nil)
可选优化:自定义编码省略空变量
如果希望当variables为nil时,JSON编码结果里不包含variables这个键(有些GraphQL服务更偏好这种格式),可以给Payload加个自定义编码逻辑:
extension Payload { enum CodingKeys: String, CodingKey { case query, variables } func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(query, forKey: .query) // 只有当variables不为nil时才编码这个键 if let variables = variables { try container.encode(variables, forKey: .variables) } } }
方案二:使用泛型结构体(强类型安全)
如果你的代码里,每个Payload的变量类型在创建时就确定,且希望编译器帮你做类型检查(避免传错变量类型),那泛型方案更合适:
// 泛型Payload,Variables约束为Codable struct Payload<Variables: Codable>: Codable { var query: String var variables: Variables? } // 变量结构体还是用你原来的定义 struct Patch: Codable { var id: String var patch: [String: String] } struct Vars1: Codable { var id: String var name: String }
使用示例
创建Payload时需要指定泛型类型(Swift通常能自动推断,也可以显式声明):
// 自动推断泛型类型为Patch let patchPayload = Payload(query: patchMutation, variables: patchData) // 显式声明泛型类型也可以 let namePayload = Payload<Vars1>(query: nameMutation, variables: nameVars)
两种方案对比
- 存在类型方案:优势是灵活性高,所有Payload实例可以统一存放在一个数组或集合里(比如
[Payload]),适合需要动态切换变量类型的场景。 - 泛型方案:优势是强类型安全,编译器会帮你检查变量类型和GraphQL查询是否匹配,避免运行时错误,但不同泛型参数的Payload是不同类型,不能直接放在同一个数组里(需要用类型擦除或者
[any Codable]间接存放)。
你可以根据自己项目的实际需求选,大部分场景下,存在类型方案已经足够灵活好用了。
内容来源于stack exchange
相关产品推荐
相关产品推荐

