与我们一起参加10月8日至10日在纽约市的活动,学习有关 GraphQL Federation 和 API 平台工程的最新技巧、趋势和新闻。参加2024年纽约市的 GraphQL Summit
文档
免费开始

自定义标量


除了其内置标量类型内建类型 (Int, String, 等.), 支持自定义类型。例如,您的模式可能提供用于 DateUUIDGeoLocation

自定义类型最初是在模式中定义的。要使用自定义类型与模式交互,您的客户端必须为每个自定义类型定义一个Swift类型。

例如,Apollo iOS会自动为所有内建类型定义Swift类型:

GraphQL类型Swift类型
IntInt
FloatDouble
Boolean布尔值
字符串字符串
标识符字符串

生成自定义标量类型

MySchema.graphqls
scalar UUID

MySchema/CustomScalars/UUID.swift
public extension MySchema {
typealias UUID = String
}

注意:自定义标量可以由GraphQL类型中引用,或作为输入参数的值,或引用输入对象。因为自定义标量仅在它们被您的引用时生成,因此它们可以在代码生成的任何未来执行中添加到项目中,而不仅仅是初始执行。

定义自定义标量类型

您可以通过创建一个新类型,或通过将类型指向另一个现有类型来定义自定义标量类型的类型。

您的自定义标量类型必须符合CustomScalarType协议。这需要您实现自定义标量类型的JSON序列化功能。


要实现CustomScalarType协议:

1. 实现属性 _jsonValue

这个属性将标量值转换为可以序列化为JSON字典的JSONValue。属性_jsonValue的值将被存储为自定义标量的缓存中的JSON值。

通常,这应该与表示自定义标量的网络响应值相同。

2. 实现初始化器 init(_jsonValue:)

此初始化器用于从JSONValue构建自定义标量值。当从网络响应构建标量时,_jsonValue参数将是网络响应中的值。当从缓存值构建标量时,这将是在_jsonValue属性中提供的值。

如果提供的值未被识别,您应该从这个函数中抛出一个错误。Apollo iOSJSONDecodingError库中提供了JSONDecodingError,但您可以抛出任何您希望抛出的自定义错误。

3. 如有需要,实现HashableEquatable协议。

如果你的自定义标量类型尚未符合HashableEquatable,你需要实现hash(into:)==操作符,分别符合这两个协议。

示例:UUID

例如,你可以将typealiasUUID指向Foundation.UUID类型:

MySchema/CustomScalars/UUID.swift
import Foundation
public extension MySchema {
typealias UUID = Foundation.UUID
}
extension Foundation.UUID: CustomScalarType {
public init (_jsonValue value: JSONValue) throws {
guard let uuidString = value as? String,
let uuid = UUID(uuidString: uuidString) else {
throw JSONDecodingError.couldNotConvert(value: value, to: Foundation.UUID.self)
}
self = uuid
}
public var _jsonValue: JSONValue {
uuidString
}
}

示例:GeoPoint

或者,你可以创建自己的自定义标量类型。在这种情况下,将typealias替换为新类型。

例如,一个自定义标量 namedGeoPoint,其JSON表示为"100.0,10.0"可以实现如下:

MySchema/CustomScalars/GeoPoint.swift
import Foundation
extension MySchema {
public struct GeoPoint: CustomScalarType, Hashable {
let x: Float
let y: Float
public init (_jsonValue value: JSONValue) throws {
let coordinates = try (value as? String)?
.components(separatedBy: ",")
.map { try Float(_jsonValue: $0) }
guard let coordinates, coordinates.count == 2 else {
throw JSONDecodingError.couldNotConvert(value: value, to: GeoPoint.self)
}
self.x = coordinates[0]
self.y = coordinates[1]
}
public var _jsonValue: JSONValue {
"\(String(format: "%.1f", x)),\(String(format: "%.1f", y))"
}
}
}

GeoPoint结构体符合CustomScalarTypeHashable。你必须显式声明符合Hashable,它继承了Equatable的符合性。因为Swift可以在这里合成HashableEquatable的符合性,所以你不需要实现它们。

init(_jsonValue:)初始化器将JSONValue转换为String并将其分离为两个坐标。它使用Float(_jsonValue:)将这些坐标转换为浮点数,这是Apollo提供的。每个内建标量类型都支持JSON序列化,你可以在自定义标量实现中使用。

为了确保序列化JSON的一致性,的_jsonValue函数确保使用String(format:,_:)将坐标格式化为使用单个小数点。

具有多个返回类型的JSON和其他自定义标量

一些自定义标量可能在运行时返回多个类型。这并不理想,因为它会失去类型安全性,但如果你使用了一个你无法控制的API,通常没有更好的替代方案。

当发生这种情况时,因为你不知道传入的类型,所以你不能为这个标量设置一个单一的typealias。相反,你需要定义另一种方式来实例化你的自定义标量对象。

这种情况最常见于JSON,它可以返回数组或字典。以下是一个例如何使用枚举来实现有限的动态类型解析(使用CustomJSON作为占位符类型名):

MySchema/CustomScalars/CustomJSON.swift
extension MySchema {
public enum CustomJSON: CustomScalarType, Hashable {
case dictionary([String: AnyHashable])
case array([AnyHashable])
public init(_jsonValue value: JSONValue) throws {
if let dict = value as? [String: AnyHashable] {
self = .dictionary(dict)
} else if let array = value as? [AnyHashable] {
self = .array(array)
} else {
throw JSONDecodingError.couldNotConvert(value: value, to: CustomJSON.self)
}
}
public var _jsonValue: JSONValue {
switch self {
case let .dictionary(json as AnyHashable),
let .array(json as AnyHashable):
return json
}
}
public static func == (lhs: CustomJSON, rhs: CustomJSON) -> Bool {
lhs._jsonValue == rhs._jsonValue
}
public func hash(into hasher: inout Hasher) {
hasher.combine(_jsonValue)
}
}
}
上一页
类型条件
下一页
持久查询
评分文章评分在GitHub上编辑编辑论坛Discord

©2024Apollo Graph Inc.,商号Apollo GraphQL。

隐私政策

公司