为Swift的graphql-codegen插件创建了graphql-codegen-swift-operations


本文是GraphQL Advent Calendar 2020的第9天。
在上一篇文章中,@ shinnoki的2021 GraphQL代码生成器将使用TypedDocumentNode。

不需要GraphQL客户端库

GraphQL通常通过在HTTP上交换JSON来实现。
换句话说,您要做的就是讲HTTP和解析JSON。
取决于语言,不使用库就可以实现这些功能。

GraphQL客户端库主要用于缓存管理。
换句话说,对于很少使用缓存的应用程序几乎不需要GraphQL客户端。

GraphQL操作环境

的TypeScript代码生成

我说我不需要客户端,但是我想要一个与响应类型相对应的codegen。
这里以使用最多工具的TypeScript为例。

GraphQL提供了一个类型函数,可以根据GraphQL模式和操作(查询,变异,订阅,片段)确定类型。

1
2
3
4
5
6
7
8
type Query {
  me: User!
}

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

有一个名为

的架构

1
2
3
query {
  me { name }
}

给定名为

的操作,响应类型在TypeScript中如下:

1
2
3
interface Data {
  me: { name: string }
}

您可以按以下方式使用它。

1
2
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操作。

https://graphql-code-generator.com/docs/plugins/typescript-operations

Swift情况下GraphQL操作的代码生成

Swift似乎有两个代码生成器:

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

由apollo-ios生成的代码是基于apollo的,如果不使用apollo的功能,我不会很高兴。
graphql_swift_gen似乎是为模式而不是为操作(查询,变异,订阅,片段)生成的。
GraphQL无法从类型中受益,因为响应会根据客户端操作的内容而变化。

在上一个示例中,

1
2
3
4
5
6
7
8
type Query {
  me: User!
}

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

即使您生成的类型直接与定义

1
2
3
4
5
6
7
8
interface Query {
  me: User
}

interface User {
  id: string
  name: string
}

并非此User的所有字段都将被返回。

1
2
3
query {
  me { name }
}

必须为

之类的操作生成它。

因此,我将graphql-codegen-swift-operations作为GraphQL Codegen的快速和操作插件??。

https://github.com/mtsmfm/graphql-codegen-swift-operations

graphql-codegen-swift-operations

有关特定的用法示例,请参阅https://github.com/mtsmfm/graphql-codegen-swift-operations/tree/master/swift-test-project。

首先,根据GraphQL Codegen教程进行设置。

https://graphql-code-generator.com/docs/getting-started/installation

接下来,npm install --save-dev @mtsmfm/graphql-codegen-swift-operations
使用@mtsmfm/graphql-codegen-swift-operations使codegen.yml生成generate.swift。

1
2
3
4
5
schema: ./schema.graphql
documents: 'graphql/*.graphql'
generates:
  Sources/app/generated.swift:
    - '@mtsmfm/graphql-codegen-swift-operations'

https://github.com/mtsmfm/graphql-codegen-swift-operations/blob/master/swift-test-project/codegen.yml

之后,进行写操作。

1
2
3
query AppQuery {
  ...
}

https://github.com/mtsmfm/graphql-codegen-swift-operations/blob/master/swift-test-project/graphql/query.graphql

运行

$(npm bin)/graphql-codegen graphql-codegen将生成generate.swift。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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]
    ...
  }
}

https://github.com/mtsmfm/graphql-codegen-swift-operations/blob/master/swift-test-project/Sources/app/generation.swift

之后,可以将AppQuery.operationDefinition用作POST的查询,并使用JSONDecoderAppQuery.self检索结果。

1
2
3
4
5
6
7
8
9
10
11
12
13
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插件时,

  • 吐在架构操作上,而不是架构上
  • 独立于特定的客户端库
  • 考虑到

    似乎很好。