我使用 graphql-codegen 插件创建了 graphql-codegen-swift-operations,这是用于 Swift 的插件

这篇文章是 GraphQL Advent Calendar 2020 的第9天的文章。
上一篇文章是由 @shinnoki 写的《如果在2021年使用GraphQL代码生成器,就使用TypedDocumentNode》。

GraphQL客户端库并非必需品。

GraphQL通常以在HTTP上进行JSON交互的方式进行实现。
换句话说,只要能够处理HTTP并解析JSON,就足够了。
在某些编程语言中,这些功能可能不需要使用库也能实现。

GraphQL客户端库主要用于缓存管理。
换句话说,如果应用程序几乎不使用缓存,则几乎不需要GraphQL客户端。

TypeScript 适用于 GraphQL 操作的代码生成情况

尽管客户不需要,但我希望有一个与响应类型相对应的 codegen 工具。此处以 TypeScript 作为最完善的工具示例。

GraphQL 提供了类型功能,可以根据 GraphQL schema 和操作(查询、变更、订阅、片段)确定类型。

type Query {
  me: User!
}

type User {
  id: ID!
  name: String!
}

有一个名为”というschema”的结构

query {
  me { name }
}

假设有一种名为「operation」的操作,如果用 TypeScript 编写该操作的响应类型,将会如下所示。

interface Data {
  me: { name: string }
}

然后您可以以以下方式使用。

const res = await fetch("/graphql", {method: "POST", body: JSON.stringify({query: "query { me { name } }"})});
const data: Data = (await res.json()).data;

尽管客户端说不需要,但是从操作 (query { me { name } }) 生成的 Data 类型可以方便地检查操作返回的响应类型是否正确。如果使用 TypeScript,graphql-codegen 的 typescript-operations 正好满足这个需求。

GraphQL 操作的 Swift 代码生成情况

似乎有两种适用于Swift的codegen选项。

    • https://github.com/apollographql/apollo-ios

 

    https://github.com/Shopify/graphql_swift_gen

如果不使用apollo的功能,apollo-ios生成的代码就不会很有用。相比之下,graphql_swift_gen似乎是根据模式生成的,而不是根据操作(查询、变更、订阅、片段)生成的。由于GraphQL对客户端操作的内容有所响应并且导致响应发生变化,所以使用这种方式无法获得类型带来的好处。

根据之前的例子来看,

type Query {
  me: User!
}

type User {
  id: ID!
  name: String!
}

即使生成出与该定义直接对应的类型,

interface Query {
  me: User
}

interface User {
  id: string
  name: string
}

不一定返回此用户所有的字段。

query {
  me { name }
}

必须针对类似于此操作进行生成。

因此,我创建了一个作为GraphQL Codegen的swift和操作插件的graphql-codegen-swift-operations。

graphql-codegen-swift-operations 翻译成中文的意思是: GraphQL代码生成- Swift操作。

具体的使用方法的示例可以在https://github.com/mtsmfm/graphql-codegen-swift-operations/tree/master/swift-test-project找到。

首先,按照 GraphQL Codegen 的教程设置。

然后,执行 npm install –save-dev @mtsmfm/graphql-codegen-swift-operations,并在 codegen.yml 中使用 @mtsmfm/graphql-codegen-swift-operations 生成 generated.swift。

schema: ./schema.graphql
documents: 'graphql/*.graphql'
generates:
  Sources/app/generated.swift:
    - '@mtsmfm/graphql-codegen-swift-operations'

接下来只需要写操作。

query AppQuery {
  ...
}

当您运行 $(npm bin)/graphql-codegen 命令来执行 graphql-codegen 时,将会生成 generated.swift 文件。

class AppQuery: Decodable {
  let data: Data?;
  let errors: Errors?;
  public static let operationDefinition: String =
    """
    query AppQuery {
      ...
    }
    """

  private enum CodingKeys: String, CodingKey {
    case data
    case errors
  }

  required init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: CodingKeys.self)
    self.data = try container.decode(Data.self, forKey: .data)
    self.errors = try container.decodeIfPresent(Errors.self, forKey: .errors)
  }


  struct Data: Decodable {
    let organizations: [Internal_1_Organizations]
    ...
  }
}

只需要使用AppQuery.operationDefinition作为查询,通过POST请求,同时使用JSONDecoder和AppQuery.self来提取结果。

let client = SynchronousHTTPClient()
do {
  let decoder = JSONDecoder()
  let data = try client.post(url: "http://localhost:4000/graphql", json: ["query": AppQuery.operationDefinition])
  let result = try decoder.decode(AppQuery.self, from: data)
  if let data = result.data {
    for org in data.organizations {
      print(org as Any)
    }
  }
} catch let error {
  ...
}

总结

也许在使用GraphQL时,会经常从选择客户端库开始,但是如果从仅仅发送请求的角度考虑,客户端库并非必需,这样或许能够看到不同的视角。另外,在创建GraphQL Codegen插件时,

    1. 不要对schema进行处理,而是对schema + operation进行处理

 

    不依赖于特定客户端库。

留意到这一点并加以注意,似乎是制作时的最佳选择。

广告
将在 10 秒后关闭
bannerAds