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

如何定义支持任意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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.08 14:54:32