From 3e727a6ea8d1d336ad8faaf9460994ee6e4658df Mon Sep 17 00:00:00 2001 From: Jay Herron Date: Thu, 18 Dec 2025 23:21:24 -0700 Subject: [PATCH 1/6] test: Adds benchmarks --- Benchmarks/Benchmarks/GraphQLBenchmarks.swift | 24 ++ Benchmarks/Benchmarks/StarWarsData.swift | 171 ++++++++++ Benchmarks/Benchmarks/StarWarsSchema.swift | 306 ++++++++++++++++++ Benchmarks/Package.resolved | 86 +++++ Benchmarks/Package.swift | 28 ++ 5 files changed, 615 insertions(+) create mode 100644 Benchmarks/Benchmarks/GraphQLBenchmarks.swift create mode 100644 Benchmarks/Benchmarks/StarWarsData.swift create mode 100644 Benchmarks/Benchmarks/StarWarsSchema.swift create mode 100644 Benchmarks/Package.resolved create mode 100644 Benchmarks/Package.swift diff --git a/Benchmarks/Benchmarks/GraphQLBenchmarks.swift b/Benchmarks/Benchmarks/GraphQLBenchmarks.swift new file mode 100644 index 00000000..d4ffc955 --- /dev/null +++ b/Benchmarks/Benchmarks/GraphQLBenchmarks.swift @@ -0,0 +1,24 @@ +import Benchmark +import GraphQL + +let benchmarks: @Sendable () -> Void = { + Benchmark("graphql") { _ in + let result = try await graphql( + schema: starWarsSchema, + request: """ + query NestedQuery { + hero { + name + friends { + name + appearsIn + friends { + name + } + } + } + } + """ + ) + } +} diff --git a/Benchmarks/Benchmarks/StarWarsData.swift b/Benchmarks/Benchmarks/StarWarsData.swift new file mode 100644 index 00000000..c7959727 --- /dev/null +++ b/Benchmarks/Benchmarks/StarWarsData.swift @@ -0,0 +1,171 @@ +import GraphQL + +/** + * This defines a basic set of data for our Star Wars Schema. + * + * This data is hard coded for the sake of the demo, but you could imagine + * fetching this data from a backend service rather than from hardcoded + * values in a more complex demo. + */ + +enum Episode: String, Encodable, Sendable { + case newHope = "NEWHOPE" + case empire = "EMPIRE" + case jedi = "JEDI" + + init?(_ string: String?) { + guard let string = string else { + return nil + } + + self.init(rawValue: string) + } +} + +protocol Character: Encodable, Sendable { + var id: String { get } + var name: String { get } + var friends: [String] { get } + var appearsIn: [Episode] { get } +} + +struct Human: Character { + let id: String + let name: String + let friends: [String] + let appearsIn: [Episode] + let homePlanet: String? + + init( + id: String, + name: String, + friends: [String], + appearsIn: [Episode], + homePlanet: String? = nil + ) { + self.id = id + self.name = name + self.friends = friends + self.appearsIn = appearsIn + self.homePlanet = homePlanet + } +} + +struct Droid: Character { + let id: String + let name: String + let friends: [String] + let appearsIn: [Episode] + let primaryFunction: String +} + +let luke = Human( + id: "1000", + name: "Luke Skywalker", + friends: ["1002", "1003", "2000", "2001"], + appearsIn: [.newHope, .empire, .jedi], + homePlanet: "Tatooine" +) + +let vader = Human( + id: "1001", + name: "Darth Vader", + friends: ["1004"], + appearsIn: [.newHope, .empire, .jedi], + homePlanet: "Tatooine" +) + +let han = Human( + id: "1002", + name: "Han Solo", + friends: ["1000", "1003", "2001"], + appearsIn: [.newHope, .empire, .jedi] +) + +let leia = Human( + id: "1003", + name: "Leia Organa", + friends: ["1000", "1002", "2000", "2001"], + appearsIn: [.newHope, .empire, .jedi], + homePlanet: "Alderaan" +) + +let tarkin = Human( + id: "1004", + name: "Wilhuff Tarkin", + friends: ["1001"], + appearsIn: [.newHope] +) + +let humanData: [String: Human] = [ + "1000": luke, + "1001": vader, + "1002": han, + "1003": leia, + "1004": tarkin, +] + +let c3po = Droid( + id: "2000", + name: "C-3PO", + friends: ["1000", "1002", "1003", "2001"], + appearsIn: [.newHope, .empire, .jedi], + primaryFunction: "Protocol" +) + +let r2d2 = Droid( + id: "2001", + name: "R2-D2", + friends: ["1000", "1002", "1003"], + appearsIn: [.newHope, .empire, .jedi], + primaryFunction: "Astromech" +) + +let droidData: [String: Droid] = [ + "2000": c3po, + "2001": r2d2, +] + +/** + * Helper function to get a character by ID. + */ +@Sendable func getCharacter(id: String) -> Character? { + return humanData[id] ?? droidData[id] +} + +/** + * Allows us to query for a character"s friends. + */ +@Sendable func getFriends(character: Character) -> [Character] { + return character.friends.reduce(into: []) { friends, friendID in + if let friend = getCharacter(id: friendID) { + friends.append(friend) + } + } +} + +/** + * Allows us to fetch the undisputed hero of the Star Wars trilogy, R2-D2. + */ +@Sendable func getHero(episode: Episode?) -> Character { + if episode == .empire { + // Luke is the hero of Episode V. + return luke + } + // R2-D2 is the hero otherwise. + return r2d2 +} + +/** + * Allows us to query for the human with the given id. + */ +@Sendable func getHuman(id: String) -> Human? { + return humanData[id] +} + +/** + * Allows us to query for the droid with the given id. + */ +@Sendable func getDroid(id: String) -> Droid? { + return droidData[id] +} diff --git a/Benchmarks/Benchmarks/StarWarsSchema.swift b/Benchmarks/Benchmarks/StarWarsSchema.swift new file mode 100644 index 00000000..23841438 --- /dev/null +++ b/Benchmarks/Benchmarks/StarWarsSchema.swift @@ -0,0 +1,306 @@ +import GraphQL + +/** + * This is designed to be an end-to-end test, demonstrating + * the full GraphQL stack. + * + * We will create a GraphQL schema that describes the major + * characters in the original Star Wars trilogy. + * + * NOTE: This may contain spoilers for the original Star + * Wars trilogy. + */ + +/** + * Using our shorthand to describe type systems, the type system for our + * Star Wars example is: + * + * enum Episode { NEWHOPE, EMPIRE, JEDI } + * + * interface Character { + * id: String! + * name: String + * friends: [Character] + * appearsIn: [Episode] + * } + * + * type Human : Character { + * id: String! + * name: String + * friends: [Character] + * appearsIn: [Episode] + * homePlanet: String + * } + * + * type Droid : Character { + * id: String! + * name: String + * friends: [Character] + * appearsIn: [Episode] + * primaryFunction: String + * } + * + * type Query { + * hero(episode: Episode): Character + * human(id: String!): Human + * droid(id: String!): Droid + * } + * + * We begin by setting up our schema. + */ + +/** + * The original trilogy consists of three movies. + * + * This implements the following type system shorthand: + * enum Episode { NEWHOPE, EMPIRE, JEDI } + */ +let EpisodeEnum = try! GraphQLEnumType( + name: "Episode", + description: "One of the films in the Star Wars Trilogy", + values: [ + "NEWHOPE": GraphQLEnumValue( + value: Map(Episode.newHope.rawValue), + description: "Released in 1977." + ), + "EMPIRE": GraphQLEnumValue( + value: Map(Episode.empire.rawValue), + description: "Released in 1980." + ), + "JEDI": GraphQLEnumValue( + value: Map(Episode.jedi.rawValue), + description: "Released in 1983." + ), + ] +) + +/** + * Characters in the Star Wars trilogy are either humans or droids. + * + * This implements the following type system shorthand: + * interface Character { + * id: String! + * name: String + * friends: [Character] + * appearsIn: [Episode] + * secretBackstory: String + * } + */ +let CharacterInterface = try! GraphQLInterfaceType( + name: "Character", + description: "A character in the Star Wars Trilogy", + fields: { [ + "id": GraphQLField( + type: GraphQLNonNull(GraphQLString), + description: "The id of the character." + ), + "name": GraphQLField( + type: GraphQLString, + description: "The name of the character." + ), + "friends": GraphQLField( + type: GraphQLList(CharacterInterface), + description: "The friends of the character, or an empty list if they have none." + ), + "appearsIn": GraphQLField( + type: GraphQLList(EpisodeEnum), + description: "Which movies they appear in." + ), + "secretBackstory": GraphQLField( + type: GraphQLString, + description: "All secrets about their past." + ), + ] }, + resolveType: { character, _ in + switch character { + case is Human: + return "Human" + default: + return "Droid" + } + } +) + +/** + * We define our human type, which implements the character interface. + * + * This implements the following type system shorthand: + * type Human : Character { + * id: String! + * name: String + * friends: [Character] + * appearsIn: [Episode] + * secretBackstory: String + * } + */ +let HumanType = try! GraphQLObjectType( + name: "Human", + description: "A humanoid creature in the Star Wars universe.", + fields: [ + "id": GraphQLField( + type: GraphQLNonNull(GraphQLString), + description: "The id of the human." + ), + "name": GraphQLField( + type: GraphQLString, + description: "The name of the human." + ), + "friends": GraphQLField( + type: GraphQLList(CharacterInterface), + description: "The friends of the human, or an empty list if they " + + "have none.", + resolve: { human, _, _, _ in + getFriends(character: human as! Human) + } + ), + "appearsIn": GraphQLField( + type: GraphQLList(EpisodeEnum), + description: "Which movies they appear in." + ), + "homePlanet": GraphQLField( + type: GraphQLString, + description: "The home planet of the human, or null if unknown." + ), + "secretBackstory": GraphQLField( + type: GraphQLString, + description: "Where are they from and how they came to be who they are.", + resolve: { _, _, _, _ in + struct Secret: Error, CustomStringConvertible { + let description: String + } + + throw Secret(description: "secretBackstory is secret.") + } + ), + ], + interfaces: [CharacterInterface], + isTypeOf: { source, _ in + source is Human + } +) + +/** + * The other type of character in Star Wars is a droid. + * + * This implements the following type system shorthand: + * type Droid : Character { + * id: String! + * name: String + * friends: [Character] + * appearsIn: [Episode] + * secretBackstory: String + * primaryFunction: String + * } + */ +let DroidType = try! GraphQLObjectType( + name: "Droid", + description: "A mechanical creature in the Star Wars universe.", + fields: [ + "id": GraphQLField( + type: GraphQLNonNull(GraphQLString), + description: "The id of the droid." + ), + "name": GraphQLField( + type: GraphQLString, + description: "The name of the droid." + ), + "friends": GraphQLField( + type: GraphQLList(CharacterInterface), + description: "The friends of the droid, or an empty list if they have none.", + resolve: { droid, _, _, _ in + getFriends(character: droid as! Droid) + } + ), + "appearsIn": GraphQLField( + type: GraphQLList(EpisodeEnum), + description: "Which movies they appear in." + ), + "secretBackstory": GraphQLField( + type: GraphQLString, + description: "Construction date and the name of the designer.", + resolve: { _, _, _, _ in + struct SecretError: Error, CustomStringConvertible { + let description: String + } + + throw SecretError(description: "secretBackstory is secret.") + } + ), + "primaryFunction": GraphQLField( + type: GraphQLString, + description: "The primary function of the droid." + ), + ], + interfaces: [CharacterInterface], + isTypeOf: { source, _ in + source is Droid + } +) + +/** + * This is the type that will be the root of our query, and the + * entry point into our schema. It gives us the ability to fetch + * objects by their IDs, as well as to fetch the undisputed hero + * of the Star Wars trilogy, R2-D2, directly. + * + * This implements the following type system shorthand: + * type Query { + * hero(episode: Episode): Character + * human(id: String!): Human + * droid(id: String!): Droid + * } + * + */ +let QueryType = try! GraphQLObjectType( + name: "Query", + fields: [ + "hero": GraphQLField( + type: CharacterInterface, + args: [ + "episode": GraphQLArgument( + type: EpisodeEnum, + description: + "If omitted, returns the hero of the whole saga. If " + + "provided, returns the hero of that particular episode." + ), + ], + resolve: { _, arguments, _, _ in + let episode = Episode(arguments["episode"].string) + return getHero(episode: episode) + } + ), + "human": GraphQLField( + type: HumanType, + args: [ + "id": GraphQLArgument( + type: GraphQLNonNull(GraphQLString), + description: "id of the human" + ), + ], + resolve: { _, arguments, _, _ in + getHuman(id: arguments["id"].string!) + } + ), + "droid": GraphQLField( + type: DroidType, + args: [ + "id": GraphQLArgument( + type: GraphQLNonNull(GraphQLString), + description: "id of the droid" + ), + ], + resolve: { _, arguments, _, _ in + getDroid(id: arguments["id"].string!) + } + ), + ] +) + +/** + * Finally, we construct our schema (whose starting query type is the query + * type we defined above) and export it. + */ +let starWarsSchema = try! GraphQLSchema( + query: QueryType, + types: [HumanType, DroidType] +) diff --git a/Benchmarks/Package.resolved b/Benchmarks/Package.resolved new file mode 100644 index 00000000..81906493 --- /dev/null +++ b/Benchmarks/Package.resolved @@ -0,0 +1,86 @@ +{ + "pins" : [ + { + "identity" : "hdrhistogram-swift", + "kind" : "remoteSourceControl", + "location" : "https://github.com/HdrHistogram/hdrhistogram-swift.git", + "state" : { + "revision" : "93a1618c8aa20f6a521a9da656a3e0591889e9dc", + "version" : "0.1.3" + } + }, + { + "identity" : "package-benchmark", + "kind" : "remoteSourceControl", + "location" : "https://github.com/ordo-one/package-benchmark", + "state" : { + "revision" : "acfd97b98f5a40d963c89437e1cfaeab8ef10bf9", + "version" : "1.29.7" + } + }, + { + "identity" : "package-jemalloc", + "kind" : "remoteSourceControl", + "location" : "https://github.com/ordo-one/package-jemalloc.git", + "state" : { + "revision" : "e8a5db026963f5bfeac842d9d3f2cc8cde323b49", + "version" : "1.0.0" + } + }, + { + "identity" : "swift-argument-parser", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-argument-parser.git", + "state" : { + "revision" : "c5d11a805e765f52ba34ec7284bd4fcd6ba68615", + "version" : "1.7.0" + } + }, + { + "identity" : "swift-atomics", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-atomics.git", + "state" : { + "revision" : "b601256eab081c0f92f059e12818ac1d4f178ff7", + "version" : "1.3.0" + } + }, + { + "identity" : "swift-collections", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-collections", + "state" : { + "revision" : "7b847a3b7008b2dc2f47ca3110d8c782fb2e5c7e", + "version" : "1.3.0" + } + }, + { + "identity" : "swift-numerics", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-numerics", + "state" : { + "revision" : "0c0290ff6b24942dadb83a929ffaaa1481df04a2", + "version" : "1.1.1" + } + }, + { + "identity" : "swift-system", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-system.git", + "state" : { + "revision" : "395a77f0aa927f0ff73941d7ac35f2b46d47c9db", + "version" : "1.6.3" + } + }, + { + "identity" : "texttable", + "kind" : "remoteSourceControl", + "location" : "https://github.com/ordo-one/TextTable.git", + "state" : { + "revision" : "a27a07300cf4ae322e0079ca0a475c5583dd575f", + "version" : "0.0.2" + } + } + ], + "version" : 2 +} diff --git a/Benchmarks/Package.swift b/Benchmarks/Package.swift new file mode 100644 index 00000000..b0b4e67a --- /dev/null +++ b/Benchmarks/Package.swift @@ -0,0 +1,28 @@ +// swift-tools-version:5.8 +import PackageDescription + +let package = Package( + name: "Benchmarks", + platforms: [.macOS(.v13)], + dependencies: [ + .package(name: "GraphQL", path: "../"), + .package( + url: "https://github.com/ordo-one/package-benchmark", + .upToNextMajor(from: "1.4.0") + ), + ], + targets: [ + .executableTarget( + name: "Benchmarks", + dependencies: [ + .product(name: "Benchmark", package: "package-benchmark"), + .product(name: "GraphQL", package: "GraphQL"), + ], + path: "Benchmarks", + plugins: [ + .plugin(name: "BenchmarkPlugin", package: "package-benchmark"), + ] + ), + ], + swiftLanguageVersions: [.v5, .version("6")] +) From ea7564e9323ed55a382ae7432208b49ebf15cc09 Mon Sep 17 00:00:00 2001 From: Jay Herron Date: Sun, 21 Dec 2025 00:25:21 -0700 Subject: [PATCH 2/6] refactor(perf): Makes static regexes global This reduces the benchmark `graphql` time by 51%, and malloc calls by 77% --- Sources/GraphQL/Error/SyntaxError.swift | 17 +++++++------- Sources/GraphQL/Language/Location.swift | 23 ++++++++----------- .../GraphQL/Utilities/AssertValidName.swift | 9 +++++--- 3 files changed, 24 insertions(+), 25 deletions(-) diff --git a/Sources/GraphQL/Error/SyntaxError.swift b/Sources/GraphQL/Error/SyntaxError.swift index b16b9e34..18f2dbe9 100644 --- a/Sources/GraphQL/Error/SyntaxError.swift +++ b/Sources/GraphQL/Error/SyntaxError.swift @@ -53,15 +53,14 @@ func splitLines(string: String) -> [String] { var location = 0 let nsstring = NSString(string: string) - do { - let regex = try NSRegularExpression(pattern: "\r\n|[\n\r]", options: []) - for match in regex.matches(in: string, options: [], range: NSRange(0 ..< nsstring.length)) { - let range = NSRange(location ..< match.range.location) - lines.append(nsstring.substring(with: range)) - location = match.range.location + match.range.length - } - } catch { - // Let lines and location remain unchanged + for match in newLineRegex.matches( + in: string, + options: [], + range: NSRange(0 ..< nsstring.length) + ) { + let range = NSRange(location ..< match.range.location) + lines.append(nsstring.substring(with: range)) + location = match.range.location + match.range.length } if lines.isEmpty { diff --git a/Sources/GraphQL/Language/Location.swift b/Sources/GraphQL/Language/Location.swift index 2c77f1c0..660d8b53 100644 --- a/Sources/GraphQL/Language/Location.swift +++ b/Sources/GraphQL/Language/Location.swift @@ -18,20 +18,17 @@ func getLocation(source: Source, position: Int) -> SourceLocation { var line = 1 var column = position + 1 - do { - let regex = try NSRegularExpression(pattern: "\r\n|[\n\r]", options: []) - let matches = regex.matches( - in: source.body, - options: [], - range: NSRange(0 ..< source.body.utf16.count) - ) - for match in matches where match.range.location < position { - line += 1 - column = position + 1 - (match.range.location + match.range.length) - } - } catch { - // Leave line and position unset if regex fails + let matches = newLineRegex.matches( + in: source.body, + options: [], + range: NSRange(0 ..< source.body.utf16.count) + ) + for match in matches where match.range.location < position { + line += 1 + column = position + 1 - (match.range.location + match.range.length) } return SourceLocation(line: line, column: column) } + +let newLineRegex = try! NSRegularExpression(pattern: "\r\n|[\n\r]", options: []) diff --git a/Sources/GraphQL/Utilities/AssertValidName.swift b/Sources/GraphQL/Utilities/AssertValidName.swift index 6bf3f6ee..5e95857d 100644 --- a/Sources/GraphQL/Utilities/AssertValidName.swift +++ b/Sources/GraphQL/Utilities/AssertValidName.swift @@ -12,9 +12,7 @@ public enum InvalidNameError: Error, CustomStringConvertible { } func assertValid(name: String) throws { - let regex = try NSRegularExpression(pattern: "^[_a-zA-Z][_a-zA-Z0-9]*$", options: []) - - let range = regex.rangeOfFirstMatch( + let range = validNameRegex.rangeOfFirstMatch( in: name, options: [], range: NSRange(0 ..< name.utf16.count) @@ -24,3 +22,8 @@ func assertValid(name: String) throws { throw InvalidNameError.invalidName(name) } } + +private let validNameRegex = try! NSRegularExpression( + pattern: "^[_a-zA-Z][_a-zA-Z0-9]*$", + options: [] +) From f8e6ea02c535b1d4ee7254473ed1f254ca8c1ffd Mon Sep 17 00:00:00 2001 From: Jay Herron Date: Mon, 22 Dec 2025 01:26:00 -0700 Subject: [PATCH 3/6] refactor(perf): Reduces validation type casting Reduces benchmark `graphql` request time by 5%, and `validateRequest` time by 13% --- .../Rules/Custom/NoDeprecatedCustomRule.swift | 23 +++++---- .../NoSchemaIntrospectionCustomRule.swift | 8 +++- .../Rules/ExecutableDefinitionsRule.swift | 8 +++- .../Rules/FieldsOnCorrectTypeRule.swift | 9 ++-- .../Rules/FragmentsOnCompositeTypesRule.swift | 11 +++-- .../KnownArgumentNamesOnDirectivesRule.swift | 11 +++-- .../Rules/KnownDirectivesRule.swift | 3 +- .../Rules/KnownFragmentNamesRule.swift | 9 ++-- .../Validation/Rules/KnownTypeNamesRule.swift | 8 +++- .../Rules/LoneAnonymousOperationRule.swift | 11 +++-- .../Rules/LoneSchemaDefinitionRule.swift | 8 +++- .../Rules/NoFragmentCyclesRule.swift | 10 ++-- .../Rules/NoUndefinedVariablesRule.swift | 8 +++- .../Rules/NoUnusedFragmentsRule.swift | 19 +++++--- .../Rules/PossibleFragmentSpreadsRule.swift | 15 +++--- .../Rules/PossibleTypeExtensionsRule.swift | 28 ++++++++--- ...dedRequiredArgumentsOnDirectivesRule.swift | 8 +++- .../Rules/ProvidedRequiredArgumentsRule.swift | 14 ++++-- .../Validation/Rules/ScalarLeafsRule.swift | 9 ++-- .../UniqueArgumentDefinitionNamesRule.swift | 24 +++++++--- .../Rules/UniqueArgumentNamesRule.swift | 48 ++++++++++++------- .../Rules/UniqueDirectiveNamesRule.swift | 8 +++- .../UniqueDirectivesPerLocationRule.swift | 14 ++++-- .../Rules/UniqueEnumValueNamesRule.swift | 12 +++-- .../UniqueFieldDefinitionNamesRule.swift | 28 ++++++++--- .../Rules/UniqueFragmentNamesRule.swift | 8 +++- .../Rules/UniqueInputFieldNamesRule.swift | 17 ++++--- .../Rules/UniqueOperationNamesRule.swift | 8 +++- .../Rules/UniqueOperationTypesRule.swift | 12 +++-- .../Rules/UniqueTypeNamesRule.swift | 30 ++++++++---- .../Rules/UniqueVariableNamesRule.swift | 8 +++- .../Rules/ValuesOfCorrectTypeRule.swift | 44 ++++++----------- .../Rules/VariablesAreInputTypesRule.swift | 8 +++- .../Validation/ValidationContext.swift | 25 +++++----- 34 files changed, 340 insertions(+), 174 deletions(-) diff --git a/Sources/GraphQL/Validation/Rules/Custom/NoDeprecatedCustomRule.swift b/Sources/GraphQL/Validation/Rules/Custom/NoDeprecatedCustomRule.swift index 14836758..5dfc4f54 100644 --- a/Sources/GraphQL/Validation/Rules/Custom/NoDeprecatedCustomRule.swift +++ b/Sources/GraphQL/Validation/Rules/Custom/NoDeprecatedCustomRule.swift @@ -12,7 +12,9 @@ public func NoDeprecatedCustomRule(context: ValidationContext) -> Visitor { return Visitor( enter: { node, _, _, _, _ in - if let node = node as? Field { + switch node.kind { + case .field: + let node = node as! Field if let fieldDef = context.fieldDef, let deprecationReason = fieldDef.deprecationReason, @@ -25,8 +27,9 @@ public func NoDeprecatedCustomRule(context: ValidationContext) -> Visitor { ) ) } - } - if let node = node as? Argument { + return .continue + case .argument: + let node = node as! Argument if let argDef = context.argument, let deprecationReason = argDef.deprecationReason @@ -50,8 +53,9 @@ public func NoDeprecatedCustomRule(context: ValidationContext) -> Visitor { ) } } - } - if let node = node as? ObjectField { + return .continue + case .objectField: + let node = node as! ObjectField if let inputObjectDef = context.parentInputType as? GraphQLInputObjectType, let inputFieldDef = try? inputObjectDef.getFields()[node.name.value], @@ -64,8 +68,9 @@ public func NoDeprecatedCustomRule(context: ValidationContext) -> Visitor { ) ) } - } - if let node = node as? EnumValue { + return .continue + case .enumValue: + let node = node as! EnumValue if let enumValueDef = context.typeInfo.enumValue, let deprecationReason = enumValueDef.deprecationReason, @@ -78,8 +83,10 @@ public func NoDeprecatedCustomRule(context: ValidationContext) -> Visitor { ) ) } + return .continue + default: + return .continue } - return .continue } ) } diff --git a/Sources/GraphQL/Validation/Rules/Custom/NoSchemaIntrospectionCustomRule.swift b/Sources/GraphQL/Validation/Rules/Custom/NoSchemaIntrospectionCustomRule.swift index c3cbd482..df72d617 100644 --- a/Sources/GraphQL/Validation/Rules/Custom/NoSchemaIntrospectionCustomRule.swift +++ b/Sources/GraphQL/Validation/Rules/Custom/NoSchemaIntrospectionCustomRule.swift @@ -12,7 +12,9 @@ public func NoSchemaIntrospectionCustomRule(context: ValidationContext) -> Visitor { return Visitor( enter: { node, _, _, _, _ in - if let node = node as? Field { + switch node.kind { + case .field: + let node = node as! Field if let type = getNamedType(type: context.type), isIntrospectionType(type: type) @@ -24,8 +26,10 @@ public func NoSchemaIntrospectionCustomRule(context: ValidationContext) -> Visit ) ) } + return .continue + default: + return .continue } - return .continue } ) } diff --git a/Sources/GraphQL/Validation/Rules/ExecutableDefinitionsRule.swift b/Sources/GraphQL/Validation/Rules/ExecutableDefinitionsRule.swift index d62fa16f..2d4101f0 100644 --- a/Sources/GraphQL/Validation/Rules/ExecutableDefinitionsRule.swift +++ b/Sources/GraphQL/Validation/Rules/ExecutableDefinitionsRule.swift @@ -28,7 +28,9 @@ func ExecutableDefinitionsRule(context: ValidationContext) -> Visitor { return Visitor( enter: { node, _, _, _, _ in - if let node = node as? Document { + switch node.kind { + case .document: + let node = node as! Document for definition in node.definitions { if !isExecutable(definition) { var defName = "schema" @@ -45,8 +47,10 @@ func ExecutableDefinitionsRule(context: ValidationContext) -> Visitor { ) } } + return .continue + default: + return .continue } - return .continue } ) } diff --git a/Sources/GraphQL/Validation/Rules/FieldsOnCorrectTypeRule.swift b/Sources/GraphQL/Validation/Rules/FieldsOnCorrectTypeRule.swift index 3994c0b4..8567bac6 100644 --- a/Sources/GraphQL/Validation/Rules/FieldsOnCorrectTypeRule.swift +++ b/Sources/GraphQL/Validation/Rules/FieldsOnCorrectTypeRule.swift @@ -24,7 +24,9 @@ func undefinedFieldMessage( func FieldsOnCorrectTypeRule(context: ValidationContext) -> Visitor { return Visitor( enter: { node, _, _, _, _ in - if let node = node as? Field { + switch node.kind { + case .field: + let node = node as! Field if let type = context.parentType { let fieldDef = context.fieldDef if fieldDef == nil { @@ -59,9 +61,10 @@ func FieldsOnCorrectTypeRule(context: ValidationContext) -> Visitor { )) } } + return .continue + default: + return .continue } - - return .continue } ) } diff --git a/Sources/GraphQL/Validation/Rules/FragmentsOnCompositeTypesRule.swift b/Sources/GraphQL/Validation/Rules/FragmentsOnCompositeTypesRule.swift index c8b6eeb0..359f9161 100644 --- a/Sources/GraphQL/Validation/Rules/FragmentsOnCompositeTypesRule.swift +++ b/Sources/GraphQL/Validation/Rules/FragmentsOnCompositeTypesRule.swift @@ -11,7 +11,9 @@ func FragmentsOnCompositeTypesRule(context: ValidationContext) -> Visitor { return Visitor( enter: { node, _, _, _, _ in - if let fragment = node as? InlineFragment { + switch node.kind { + case .inlineFragment: + let fragment = node as! InlineFragment if let typeCondition = fragment.typeCondition { if let type = typeFromAST(schema: context.schema, inputTypeAST: typeCondition) { if type is GraphQLCompositeType { @@ -28,8 +30,8 @@ func FragmentsOnCompositeTypesRule(context: ValidationContext) -> Visitor { } } return .continue - } - if let fragment = node as? FragmentDefinition { + case .fragmentDefinition: + let fragment = node as! FragmentDefinition let typeCondition = fragment.typeCondition if let type = typeFromAST(schema: context.schema, inputTypeAST: typeCondition) { if type is GraphQLCompositeType { @@ -45,8 +47,9 @@ func FragmentsOnCompositeTypesRule(context: ValidationContext) -> Visitor { ) } return .continue + default: + return .continue } - return .continue } ) } diff --git a/Sources/GraphQL/Validation/Rules/KnownArgumentNamesOnDirectivesRule.swift b/Sources/GraphQL/Validation/Rules/KnownArgumentNamesOnDirectivesRule.swift index df62b18f..81ccb36f 100644 --- a/Sources/GraphQL/Validation/Rules/KnownArgumentNamesOnDirectivesRule.swift +++ b/Sources/GraphQL/Validation/Rules/KnownArgumentNamesOnDirectivesRule.swift @@ -11,7 +11,8 @@ func KnownArgumentNamesOnDirectivesRule( let astDefinitions = context.ast.definitions for def in astDefinitions { - if let def = def as? DirectiveDefinition { + if def.kind == .directiveDefinition { + let def = def as! DirectiveDefinition let argsNodes = def.arguments directiveArgs[def.name.value] = argsNodes.map(\.name.value) } @@ -19,7 +20,9 @@ func KnownArgumentNamesOnDirectivesRule( return Visitor( enter: { node, _, _, _, _ in - if let directiveNode = node as? Directive { + switch node.kind { + case .directive: + let directiveNode = node as! Directive let directiveName = directiveNode.name.value let knownArgs = directiveArgs[directiveName] @@ -38,8 +41,10 @@ func KnownArgumentNamesOnDirectivesRule( } } } + return .continue + default: + return .continue } - return .continue } ) } diff --git a/Sources/GraphQL/Validation/Rules/KnownDirectivesRule.swift b/Sources/GraphQL/Validation/Rules/KnownDirectivesRule.swift index 2e805254..8240fb1d 100644 --- a/Sources/GraphQL/Validation/Rules/KnownDirectivesRule.swift +++ b/Sources/GraphQL/Validation/Rules/KnownDirectivesRule.swift @@ -25,7 +25,8 @@ func KnownDirectivesRule(context: SDLorNormalValidationContext) -> Visitor { return Visitor( enter: { node, _, _, _, ancestors in - guard let node = node as? Directive else { return .continue } + guard node.kind == .directive else { return .continue } + let node = node as! Directive let name = node.name.value diff --git a/Sources/GraphQL/Validation/Rules/KnownFragmentNamesRule.swift b/Sources/GraphQL/Validation/Rules/KnownFragmentNamesRule.swift index a58591e4..6821b9f1 100644 --- a/Sources/GraphQL/Validation/Rules/KnownFragmentNamesRule.swift +++ b/Sources/GraphQL/Validation/Rules/KnownFragmentNamesRule.swift @@ -11,7 +11,9 @@ import Foundation func KnownFragmentNamesRule(context: ValidationContext) -> Visitor { return Visitor( enter: { node, _, _, _, _ in - if let fragmentReference = node as? FragmentSpread { + switch node.kind { + case .fragmentSpread: + let fragmentReference = node as! FragmentSpread let fragmentName = fragmentReference.name.value let fragmentDefinition = context.getFragment(name: fragmentName) @@ -21,9 +23,10 @@ func KnownFragmentNamesRule(context: ValidationContext) -> Visitor { nodes: [fragmentReference.name] )) } + return .continue + default: + return .continue } - - return .continue } ) } diff --git a/Sources/GraphQL/Validation/Rules/KnownTypeNamesRule.swift b/Sources/GraphQL/Validation/Rules/KnownTypeNamesRule.swift index 6c47ea52..c8b276d8 100644 --- a/Sources/GraphQL/Validation/Rules/KnownTypeNamesRule.swift +++ b/Sources/GraphQL/Validation/Rules/KnownTypeNamesRule.swift @@ -28,7 +28,9 @@ func KnownTypeNamesRule(context: SDLorNormalValidationContext) -> Visitor { return Visitor( enter: { node, _, parent, _, ancestors in - if let type = node as? NamedType { + switch node.kind { + case .namedType: + let type = node as! NamedType let typeName = type.name.value if !typeNames.contains(typeName) { let definitionNode = ancestors.count > 2 ? ancestors[2] : parent @@ -52,8 +54,10 @@ func KnownTypeNamesRule(context: SDLorNormalValidationContext) -> Visitor { ) ) } + return .continue + default: + return .continue } - return .continue } ) } diff --git a/Sources/GraphQL/Validation/Rules/LoneAnonymousOperationRule.swift b/Sources/GraphQL/Validation/Rules/LoneAnonymousOperationRule.swift index 783327ce..a2588a87 100644 --- a/Sources/GraphQL/Validation/Rules/LoneAnonymousOperationRule.swift +++ b/Sources/GraphQL/Validation/Rules/LoneAnonymousOperationRule.swift @@ -11,11 +11,13 @@ func LoneAnonymousOperationRule(context: ValidationContext) -> Visitor { var operationCount = 0 return Visitor( enter: { node, _, _, _, _ in - if let document = node as? Document { + switch node.kind { + case .document: + let document = node as! Document operationCount = document.definitions.filter { $0 is OperationDefinition }.count return .continue - } - if let operation = node as? OperationDefinition { + case .operationDefinition: + let operation = node as! OperationDefinition if operation.name == nil, operationCount > 1 { context.report( error: GraphQLError( @@ -25,8 +27,9 @@ func LoneAnonymousOperationRule(context: ValidationContext) -> Visitor { ) } return .continue + default: + return .continue } - return .continue } ) } diff --git a/Sources/GraphQL/Validation/Rules/LoneSchemaDefinitionRule.swift b/Sources/GraphQL/Validation/Rules/LoneSchemaDefinitionRule.swift index b1aa4428..f922ea0d 100644 --- a/Sources/GraphQL/Validation/Rules/LoneSchemaDefinitionRule.swift +++ b/Sources/GraphQL/Validation/Rules/LoneSchemaDefinitionRule.swift @@ -15,7 +15,9 @@ func LoneSchemaDefinitionRule(context: SDLValidationContext) -> Visitor { var schemaDefinitionsCount = 0 return Visitor( enter: { node, _, _, _, _ in - if let node = node as? SchemaDefinition { + switch node.kind { + case .schemaDefinition: + let node = node as! SchemaDefinition if alreadyDefined { context.report( error: GraphQLError( @@ -35,8 +37,10 @@ func LoneSchemaDefinitionRule(context: SDLValidationContext) -> Visitor { } schemaDefinitionsCount = schemaDefinitionsCount + 1 + return .continue + default: + return .continue } - return .continue } ) } diff --git a/Sources/GraphQL/Validation/Rules/NoFragmentCyclesRule.swift b/Sources/GraphQL/Validation/Rules/NoFragmentCyclesRule.swift index acf56315..f2a47b3d 100644 --- a/Sources/GraphQL/Validation/Rules/NoFragmentCyclesRule.swift +++ b/Sources/GraphQL/Validation/Rules/NoFragmentCyclesRule.swift @@ -66,14 +66,16 @@ func NoFragmentCyclesRule(context: ValidationContext) -> Visitor { return Visitor( enter: { node, _, _, _, _ in - if node is OperationDefinition { + switch node.kind { + case .operationDefinition: return .skip - } - if let fragmentDefinition = node as? FragmentDefinition { + case .fragmentDefinition: + let fragmentDefinition = node as! FragmentDefinition detectCycleRecursive(fragment: fragmentDefinition) return .skip + default: + return .continue } - return .continue } ) } diff --git a/Sources/GraphQL/Validation/Rules/NoUndefinedVariablesRule.swift b/Sources/GraphQL/Validation/Rules/NoUndefinedVariablesRule.swift index babe99ef..9e51193f 100644 --- a/Sources/GraphQL/Validation/Rules/NoUndefinedVariablesRule.swift +++ b/Sources/GraphQL/Validation/Rules/NoUndefinedVariablesRule.swift @@ -10,7 +10,9 @@ func NoUndefinedVariablesRule(context: ValidationContext) -> Visitor { return Visitor( enter: { node, _, _, _, _ in - if let operation = node as? OperationDefinition { + switch node.kind { + case .operationDefinition: + let operation = node as! OperationDefinition let variableNameDefined = Set( operation.variableDefinitions.map { $0.variable.name.value } ) @@ -35,8 +37,10 @@ func NoUndefinedVariablesRule(context: ValidationContext) -> Visitor { ) } } + return .continue + default: + return .continue } - return .continue } ) } diff --git a/Sources/GraphQL/Validation/Rules/NoUnusedFragmentsRule.swift b/Sources/GraphQL/Validation/Rules/NoUnusedFragmentsRule.swift index 05e2a868..7680396f 100644 --- a/Sources/GraphQL/Validation/Rules/NoUnusedFragmentsRule.swift +++ b/Sources/GraphQL/Validation/Rules/NoUnusedFragmentsRule.swift @@ -13,22 +13,25 @@ func NoUnusedFragmentsRule(context: ValidationContext) -> Visitor { return Visitor( enter: { node, _, _, _, _ in - if let operation = node as? OperationDefinition { + switch node.kind { + case .operationDefinition: + let operation = node as! OperationDefinition for fragment in context.getRecursivelyReferencedFragments(operation: operation) { fragmentNameUsed.insert(fragment.name.value) } return .continue - } - - if let fragment = node as? FragmentDefinition { + case .fragmentDefinition: + let fragment = node as! FragmentDefinition fragmentDefs.append(fragment) return .continue + default: + return .continue } - return .continue }, leave: { node, _, _, _, _ -> VisitResult in // Use Document as proxy for the end of the visitation - if node is Document { + switch node.kind { + case .document: for fragmentDef in fragmentDefs { let fragName = fragmentDef.name.value if !fragmentNameUsed.contains(fragName) { @@ -40,8 +43,10 @@ func NoUnusedFragmentsRule(context: ValidationContext) -> Visitor { ) } } + return .continue + default: + return .continue } - return .continue } ) } diff --git a/Sources/GraphQL/Validation/Rules/PossibleFragmentSpreadsRule.swift b/Sources/GraphQL/Validation/Rules/PossibleFragmentSpreadsRule.swift index 015efd5b..4d3de756 100644 --- a/Sources/GraphQL/Validation/Rules/PossibleFragmentSpreadsRule.swift +++ b/Sources/GraphQL/Validation/Rules/PossibleFragmentSpreadsRule.swift @@ -8,7 +8,9 @@ func PossibleFragmentSpreadsRule(context: ValidationContext) -> Visitor { return Visitor( enter: { node, _, _, _, _ in - if let node = node as? InlineFragment { + switch node.kind { + case .inlineFragment: + let node = node as! InlineFragment guard let fragType = context.type as? GraphQLCompositeType, let parentType = context.parentType @@ -32,9 +34,9 @@ func PossibleFragmentSpreadsRule(context: ValidationContext) -> Visitor { nodes: [node] ) ) - } - - if let node = node as? FragmentSpread { + return .continue + case .fragmentSpread: + let node = node as! FragmentSpread let fragName = node.name.value guard @@ -60,9 +62,10 @@ func PossibleFragmentSpreadsRule(context: ValidationContext) -> Visitor { nodes: [node] ) ) + return .continue + default: + return .continue } - - return .continue } ) } diff --git a/Sources/GraphQL/Validation/Rules/PossibleTypeExtensionsRule.swift b/Sources/GraphQL/Validation/Rules/PossibleTypeExtensionsRule.swift index 9e5d3a53..055e4373 100644 --- a/Sources/GraphQL/Validation/Rules/PossibleTypeExtensionsRule.swift +++ b/Sources/GraphQL/Validation/Rules/PossibleTypeExtensionsRule.swift @@ -18,20 +18,34 @@ func PossibleTypeExtensionsRule( return Visitor( enter: { node, _, _, _, _ in - if let node = node as? ScalarExtensionDefinition { + switch node.kind { + case .scalarExtensionDefinition: + let node = node as! ScalarExtensionDefinition checkExtension(node: node) - } else if let node = node as? TypeExtensionDefinition { + return .continue + case .typeExtensionDefinition: + let node = node as! TypeExtensionDefinition checkExtension(node: node) - } else if let node = node as? InterfaceExtensionDefinition { + return .continue + case .interfaceExtensionDefinition: + let node = node as! InterfaceExtensionDefinition checkExtension(node: node) - } else if let node = node as? UnionExtensionDefinition { + return .continue + case .unionExtensionDefinition: + let node = node as! UnionExtensionDefinition checkExtension(node: node) - } else if let node = node as? EnumExtensionDefinition { + return .continue + case .enumExtensionDefinition: + let node = node as! EnumExtensionDefinition checkExtension(node: node) - } else if let node = node as? InputObjectExtensionDefinition { + return .continue + case .inputObjectExtensionDefinition: + let node = node as! InputObjectExtensionDefinition checkExtension(node: node) + return .continue + default: + return .continue } - return .continue } ) diff --git a/Sources/GraphQL/Validation/Rules/ProvidedRequiredArgumentsOnDirectivesRule.swift b/Sources/GraphQL/Validation/Rules/ProvidedRequiredArgumentsOnDirectivesRule.swift index f4211e15..b9319a91 100644 --- a/Sources/GraphQL/Validation/Rules/ProvidedRequiredArgumentsOnDirectivesRule.swift +++ b/Sources/GraphQL/Validation/Rules/ProvidedRequiredArgumentsOnDirectivesRule.swift @@ -29,7 +29,9 @@ func ProvidedRequiredArgumentsOnDirectivesRule( return Visitor( // Validate on leave to allow for deeper errors to appear first. leave: { node, _, _, _, _ in - if let directiveNode = node as? Directive { + switch node.kind { + case .directive: + let directiveNode = node as! Directive let directiveName = directiveNode.name.value if let requiredArgs = requiredArgsMap[directiveName] { let argNodes = directiveNode.arguments @@ -45,8 +47,10 @@ func ProvidedRequiredArgumentsOnDirectivesRule( } } } + return .continue + default: + return .continue } - return .continue } ) } diff --git a/Sources/GraphQL/Validation/Rules/ProvidedRequiredArgumentsRule.swift b/Sources/GraphQL/Validation/Rules/ProvidedRequiredArgumentsRule.swift index 85953b59..296b867e 100644 --- a/Sources/GraphQL/Validation/Rules/ProvidedRequiredArgumentsRule.swift +++ b/Sources/GraphQL/Validation/Rules/ProvidedRequiredArgumentsRule.swift @@ -37,7 +37,9 @@ func ProvidedRequiredArgumentsRule(context: ValidationContext) -> Visitor { return Visitor( leave: { node, _, _, _, _ in - if let fieldNode = node as? Field { + switch node.kind { + case .field: + let fieldNode = node as! Field guard let fieldDef = context.fieldDef else { return .continue } @@ -52,9 +54,9 @@ func ProvidedRequiredArgumentsRule(context: ValidationContext) -> Visitor { )) } } - } - - if let directiveNode = node as? Directive { + return .continue + case .directive: + let directiveNode = node as! Directive let directiveName = directiveNode.name.value if let requiredArgs = requiredArgsMap[directiveName] { @@ -69,8 +71,10 @@ func ProvidedRequiredArgumentsRule(context: ValidationContext) -> Visitor { } } } + return .continue + default: + return .continue } - return .continue } ) } diff --git a/Sources/GraphQL/Validation/Rules/ScalarLeafsRule.swift b/Sources/GraphQL/Validation/Rules/ScalarLeafsRule.swift index 201b40fb..b381c4ef 100644 --- a/Sources/GraphQL/Validation/Rules/ScalarLeafsRule.swift +++ b/Sources/GraphQL/Validation/Rules/ScalarLeafsRule.swift @@ -17,7 +17,9 @@ func requiredSubselectionMessage(fieldName: String, type: GraphQLType) -> String func ScalarLeafsRule(context: ValidationContext) -> Visitor { return Visitor( enter: { node, _, _, _, _ in - if let node = node as? Field { + switch node.kind { + case .field: + let node = node as! Field if let type = context.type { if isLeafType(type: getNamedType(type: type)) { if let selectionSet = node.selectionSet { @@ -41,9 +43,10 @@ func ScalarLeafsRule(context: ValidationContext) -> Visitor { context.report(error: error) } } + return .continue + default: + return .continue } - - return .continue } ) } diff --git a/Sources/GraphQL/Validation/Rules/UniqueArgumentDefinitionNamesRule.swift b/Sources/GraphQL/Validation/Rules/UniqueArgumentDefinitionNamesRule.swift index 76ea7e2b..1f97dfaa 100644 --- a/Sources/GraphQL/Validation/Rules/UniqueArgumentDefinitionNamesRule.swift +++ b/Sources/GraphQL/Validation/Rules/UniqueArgumentDefinitionNamesRule.swift @@ -10,28 +10,40 @@ func UniqueArgumentDefinitionNamesRule( ) -> Visitor { return Visitor( enter: { node, _, _, _, _ in - if let directiveNode = node as? DirectiveDefinition { + switch node.kind { + case .directiveDefinition: + let directiveNode = node as! DirectiveDefinition let argumentNodes = directiveNode.arguments checkArgUniqueness( parentName: "@\(directiveNode.name.value)", argumentNodes: argumentNodes ) - } else if let node = node as? InterfaceTypeDefinition { + return .continue + case .interfaceTypeDefinition: + let node = node as! InterfaceTypeDefinition checkArgUniquenessPerField(name: node.name, fields: node.fields) - } else if let node = node as? InterfaceExtensionDefinition { + return .continue + case .interfaceExtensionDefinition: + let node = node as! InterfaceExtensionDefinition checkArgUniquenessPerField( name: node.definition.name, fields: node.definition.fields ) - } else if let node = node as? ObjectTypeDefinition { + return .continue + case .objectTypeDefinition: + let node = node as! ObjectTypeDefinition checkArgUniquenessPerField(name: node.name, fields: node.fields) - } else if let node = node as? TypeExtensionDefinition { + return .continue + case .typeExtensionDefinition: + let node = node as! TypeExtensionDefinition checkArgUniquenessPerField( name: node.definition.name, fields: node.definition.fields ) + return .continue + default: + return .continue } - return .continue } ) diff --git a/Sources/GraphQL/Validation/Rules/UniqueArgumentNamesRule.swift b/Sources/GraphQL/Validation/Rules/UniqueArgumentNamesRule.swift index 20445a3f..3417560c 100644 --- a/Sources/GraphQL/Validation/Rules/UniqueArgumentNamesRule.swift +++ b/Sources/GraphQL/Validation/Rules/UniqueArgumentNamesRule.swift @@ -10,28 +10,42 @@ func UniqueArgumentNamesRule(context: ASTValidationContext) -> Visitor { return Visitor( enter: { node, _, _, _, _ in - let argumentNodes: [Argument] - if let field = node as? Field { - argumentNodes = field.arguments - } else if let directive = node as? Directive { - argumentNodes = directive.arguments - } else { - return .continue - } + switch node.kind { + case .field: + let field = node as! Field + let argumentNodes = field.arguments + let seenArgs = Dictionary(grouping: argumentNodes) { $0.name.value } - let seenArgs = Dictionary(grouping: argumentNodes) { $0.name.value } + for (argName, argNodes) in seenArgs { + if argNodes.count > 1 { + context.report( + error: GraphQLError( + message: "There can be only one argument named \"\(argName)\".", + nodes: argNodes.map { $0.name } + ) + ) + } + } + return .continue + case .directive: + let directive = node as! Directive + let argumentNodes = directive.arguments + let seenArgs = Dictionary(grouping: argumentNodes) { $0.name.value } - for (argName, argNodes) in seenArgs { - if argNodes.count > 1 { - context.report( - error: GraphQLError( - message: "There can be only one argument named \"\(argName)\".", - nodes: argNodes.map { $0.name } + for (argName, argNodes) in seenArgs { + if argNodes.count > 1 { + context.report( + error: GraphQLError( + message: "There can be only one argument named \"\(argName)\".", + nodes: argNodes.map { $0.name } + ) ) - ) + } } + return .continue + default: + return .continue } - return .continue } ) } diff --git a/Sources/GraphQL/Validation/Rules/UniqueDirectiveNamesRule.swift b/Sources/GraphQL/Validation/Rules/UniqueDirectiveNamesRule.swift index 083f92aa..5b44ee0b 100644 --- a/Sources/GraphQL/Validation/Rules/UniqueDirectiveNamesRule.swift +++ b/Sources/GraphQL/Validation/Rules/UniqueDirectiveNamesRule.swift @@ -12,7 +12,9 @@ func UniqueDirectiveNamesRule( return Visitor( enter: { node, _, _, _, _ in - if let node = node as? DirectiveDefinition { + switch node.kind { + case .directiveDefinition: + let node = node as! DirectiveDefinition let directiveName = node.name.value if schema?.getDirective(name: directiveName) != nil { context.report( @@ -33,8 +35,10 @@ func UniqueDirectiveNamesRule( } else { knownDirectiveNames[directiveName] = node.name } + return .continue + default: + return .continue } - return .continue } ) } diff --git a/Sources/GraphQL/Validation/Rules/UniqueDirectivesPerLocationRule.swift b/Sources/GraphQL/Validation/Rules/UniqueDirectivesPerLocationRule.swift index d5393973..8e2296f3 100644 --- a/Sources/GraphQL/Validation/Rules/UniqueDirectivesPerLocationRule.swift +++ b/Sources/GraphQL/Validation/Rules/UniqueDirectivesPerLocationRule.swift @@ -38,16 +38,24 @@ func UniqueDirectivesPerLocationRule(context: SDLorNormalValidationContext) -> V let directives = directiveNodes as? [Directive] { var seenDirectives = [String: Directive]() - if node.kind == .schemaDefinition || node.kind == .schemaExtensionDefinition { + switch node.kind { + case .schemaDefinition, .schemaExtensionDefinition: seenDirectives = schemaDirectives - } else if let node = node as? TypeDefinition { + case .enumTypeDefinition, .unionTypeDefinition, .objectTypeDefinition, + .scalarTypeDefinition, .interfaceTypeDefinition, .operationTypeDefinition, + .inputObjectTypeDefinition: + let node = node as! TypeDefinition let typeName = node.name.value seenDirectives = typeDirectivesMap[typeName] ?? [:] typeDirectivesMap[typeName] = seenDirectives - } else if let node = node as? TypeExtensionDefinition { + case .typeExtensionDefinition: + let node = node as! TypeExtensionDefinition let typeName = node.definition.name.value seenDirectives = typeDirectivesMap[typeName] ?? [:] typeDirectivesMap[typeName] = seenDirectives + default: + // Do nothing + break } for directive in directives { diff --git a/Sources/GraphQL/Validation/Rules/UniqueEnumValueNamesRule.swift b/Sources/GraphQL/Validation/Rules/UniqueEnumValueNamesRule.swift index 9f162849..2bf50b85 100644 --- a/Sources/GraphQL/Validation/Rules/UniqueEnumValueNamesRule.swift +++ b/Sources/GraphQL/Validation/Rules/UniqueEnumValueNamesRule.swift @@ -13,12 +13,18 @@ func UniqueEnumValueNamesRule( return Visitor( enter: { node, _, _, _, _ in - if let definition = node as? EnumTypeDefinition { + switch node.kind { + case .enumTypeDefinition: + let definition = node as! EnumTypeDefinition checkValueUniqueness(node: definition) - } else if let definition = node as? EnumExtensionDefinition { + return .continue + case .enumExtensionDefinition: + let definition = node as! EnumExtensionDefinition checkValueUniqueness(node: definition.definition) + return .continue + default: + return .continue } - return .continue } ) diff --git a/Sources/GraphQL/Validation/Rules/UniqueFieldDefinitionNamesRule.swift b/Sources/GraphQL/Validation/Rules/UniqueFieldDefinitionNamesRule.swift index ff8f8333..bd39fe04 100644 --- a/Sources/GraphQL/Validation/Rules/UniqueFieldDefinitionNamesRule.swift +++ b/Sources/GraphQL/Validation/Rules/UniqueFieldDefinitionNamesRule.swift @@ -13,20 +13,34 @@ func UniqueFieldDefinitionNamesRule( return Visitor( enter: { node, _, _, _, _ in - if let node = node as? InputObjectTypeDefinition { + switch node.kind { + case .inputObjectTypeDefinition: + let node = node as! InputObjectTypeDefinition checkFieldUniqueness(name: node.name, fields: node.fields) - } else if let node = node as? InputObjectExtensionDefinition { + return .continue + case .inputObjectExtensionDefinition: + let node = node as! InputObjectExtensionDefinition checkFieldUniqueness(name: node.name, fields: node.definition.fields) - } else if let node = node as? InterfaceTypeDefinition { + return .continue + case .interfaceTypeDefinition: + let node = node as! InterfaceTypeDefinition checkFieldUniqueness(name: node.name, fields: node.fields) - } else if let node = node as? InterfaceExtensionDefinition { + return .continue + case .interfaceExtensionDefinition: + let node = node as! InterfaceExtensionDefinition checkFieldUniqueness(name: node.name, fields: node.definition.fields) - } else if let node = node as? ObjectTypeDefinition { + return .continue + case .objectTypeDefinition: + let node = node as! ObjectTypeDefinition checkFieldUniqueness(name: node.name, fields: node.fields) - } else if let node = node as? TypeExtensionDefinition { + return .continue + case .typeExtensionDefinition: + let node = node as! TypeExtensionDefinition checkFieldUniqueness(name: node.name, fields: node.definition.fields) + return .continue + default: + return .continue } - return .continue } ) diff --git a/Sources/GraphQL/Validation/Rules/UniqueFragmentNamesRule.swift b/Sources/GraphQL/Validation/Rules/UniqueFragmentNamesRule.swift index 905b09a0..ef6810ec 100644 --- a/Sources/GraphQL/Validation/Rules/UniqueFragmentNamesRule.swift +++ b/Sources/GraphQL/Validation/Rules/UniqueFragmentNamesRule.swift @@ -10,7 +10,9 @@ func UniqueFragmentNamesRule(context: ValidationContext) -> Visitor { var knownFragmentNames = [String: Name]() return Visitor( enter: { node, _, _, _, _ in - if let fragment = node as? FragmentDefinition { + switch node.kind { + case .fragmentDefinition: + let fragment = node as! FragmentDefinition let fragmentName = fragment.name if let knownFragmentName = knownFragmentNames[fragmentName.value] { context.report( @@ -22,8 +24,10 @@ func UniqueFragmentNamesRule(context: ValidationContext) -> Visitor { } else { knownFragmentNames[fragmentName.value] = fragmentName } + return .continue + default: + return .continue } - return .continue } ) } diff --git a/Sources/GraphQL/Validation/Rules/UniqueInputFieldNamesRule.swift b/Sources/GraphQL/Validation/Rules/UniqueInputFieldNamesRule.swift index 374eea72..076a8915 100644 --- a/Sources/GraphQL/Validation/Rules/UniqueInputFieldNamesRule.swift +++ b/Sources/GraphQL/Validation/Rules/UniqueInputFieldNamesRule.swift @@ -13,12 +13,13 @@ func UniqueInputFieldNamesRule(context: ASTValidationContext) -> Visitor { return Visitor( enter: { node, _, _, _, _ in - if node is ObjectValue { + switch node.kind { + case .objectValue: knownNameStack.append(knownNames) knownNames = [:] return .continue - } - if let objectField = node as? ObjectField { + case .objectField: + let objectField = node as! ObjectField let fieldName = objectField.name.value if let knownName = knownNames[fieldName] { context.report( @@ -31,15 +32,19 @@ func UniqueInputFieldNamesRule(context: ASTValidationContext) -> Visitor { knownNames[fieldName] = objectField.name } return .continue + default: + return .continue } - return .continue }, leave: { node, _, _, _, _ in - if node is ObjectValue { + switch node.kind { + case .objectValue: let prevKnownNames = knownNameStack.popLast() knownNames = prevKnownNames ?? [:] + return .continue + default: + return .continue } - return .continue } ) } diff --git a/Sources/GraphQL/Validation/Rules/UniqueOperationNamesRule.swift b/Sources/GraphQL/Validation/Rules/UniqueOperationNamesRule.swift index 60b91438..2ff7f786 100644 --- a/Sources/GraphQL/Validation/Rules/UniqueOperationNamesRule.swift +++ b/Sources/GraphQL/Validation/Rules/UniqueOperationNamesRule.swift @@ -10,7 +10,9 @@ func UniqueOperationNamesRule(context: ValidationContext) -> Visitor { var knownOperationNames = [String: Name]() return Visitor( enter: { node, _, _, _, _ in - if let operation = node as? OperationDefinition { + switch node.kind { + case .operationDefinition: + let operation = node as! OperationDefinition if let operationName = operation.name { if let knownOperationName = knownOperationNames[operationName.value] { context.report( @@ -23,8 +25,10 @@ func UniqueOperationNamesRule(context: ValidationContext) -> Visitor { knownOperationNames[operationName.value] = operationName } } + return .continue + default: + return .continue } - return .continue } ) } diff --git a/Sources/GraphQL/Validation/Rules/UniqueOperationTypesRule.swift b/Sources/GraphQL/Validation/Rules/UniqueOperationTypesRule.swift index 95e52787..c438cae5 100644 --- a/Sources/GraphQL/Validation/Rules/UniqueOperationTypesRule.swift +++ b/Sources/GraphQL/Validation/Rules/UniqueOperationTypesRule.swift @@ -25,12 +25,18 @@ func UniqueOperationTypesRule( return Visitor( enter: { node, _, _, _, _ in - if let operation = node as? SchemaDefinition { + switch node.kind { + case .schemaDefinition: + let operation = node as! SchemaDefinition checkOperationTypes(operation.operationTypes) - } else if let operation = node as? SchemaExtensionDefinition { + return .continue + case .schemaExtensionDefinition: + let operation = node as! SchemaExtensionDefinition checkOperationTypes(operation.definition.operationTypes) + return .continue + default: + return .continue } - return .continue } ) diff --git a/Sources/GraphQL/Validation/Rules/UniqueTypeNamesRule.swift b/Sources/GraphQL/Validation/Rules/UniqueTypeNamesRule.swift index de20290b..427deeee 100644 --- a/Sources/GraphQL/Validation/Rules/UniqueTypeNamesRule.swift +++ b/Sources/GraphQL/Validation/Rules/UniqueTypeNamesRule.swift @@ -10,22 +10,34 @@ func UniqueTypeNamesRule(context: SDLValidationContext) -> Visitor { return Visitor( enter: { node, _, _, _, _ in - if let definition = node as? ScalarTypeDefinition { + switch node.kind { + case .scalarTypeDefinition: + let definition = node as! ScalarTypeDefinition checkTypeName(node: definition) - } else if let definition = node as? ObjectTypeDefinition { + return .continue + case .objectTypeDefinition: + let definition = node as! ObjectTypeDefinition checkTypeName(node: definition) - } else if let definition = node as? InterfaceTypeDefinition { + return .continue + case .interfaceTypeDefinition: + let definition = node as! InterfaceTypeDefinition checkTypeName(node: definition) - } else if let definition = node as? InterfaceTypeDefinition { + return .continue + case .unionTypeDefinition: + let definition = node as! UnionTypeDefinition checkTypeName(node: definition) - } else if let definition = node as? UnionTypeDefinition { + return .continue + case .enumTypeDefinition: + let definition = node as! EnumTypeDefinition checkTypeName(node: definition) - } else if let definition = node as? EnumTypeDefinition { - checkTypeName(node: definition) - } else if let definition = node as? InputObjectTypeDefinition { + return .continue + case .inputObjectTypeDefinition: + let definition = node as! InputObjectTypeDefinition checkTypeName(node: definition) + return .continue + default: + return .continue } - return .continue } ) diff --git a/Sources/GraphQL/Validation/Rules/UniqueVariableNamesRule.swift b/Sources/GraphQL/Validation/Rules/UniqueVariableNamesRule.swift index 1339304e..5c2d5782 100644 --- a/Sources/GraphQL/Validation/Rules/UniqueVariableNamesRule.swift +++ b/Sources/GraphQL/Validation/Rules/UniqueVariableNamesRule.swift @@ -7,7 +7,9 @@ func UniqueVariableNamesRule(context: ValidationContext) -> Visitor { return Visitor( enter: { node, _, _, _, _ in - if let operation = node as? OperationDefinition { + switch node.kind { + case .operationDefinition: + let operation = node as! OperationDefinition let variableDefinitions = operation.variableDefinitions let seenVariableDefinitions = Dictionary(grouping: variableDefinitions) { node in @@ -24,8 +26,10 @@ func UniqueVariableNamesRule(context: ValidationContext) -> Visitor { ) } } + return .continue + default: + return .continue } - return .continue } ) } diff --git a/Sources/GraphQL/Validation/Rules/ValuesOfCorrectTypeRule.swift b/Sources/GraphQL/Validation/Rules/ValuesOfCorrectTypeRule.swift index 4ce84608..494169d9 100644 --- a/Sources/GraphQL/Validation/Rules/ValuesOfCorrectTypeRule.swift +++ b/Sources/GraphQL/Validation/Rules/ValuesOfCorrectTypeRule.swift @@ -12,15 +12,16 @@ func ValuesOfCorrectTypeRule(context: ValidationContext) -> Visitor { return Visitor( enter: { node, _, _, _, _ in - if node is OperationDefinition { + switch node.kind { + case .operationDefinition: variableDefinitions = [:] return .continue - } - if let variableDefinition = node as? VariableDefinition { + case .variableDefinition: + let variableDefinition = node as! VariableDefinition variableDefinitions[variableDefinition.variable.name.value] = variableDefinition return .continue - } - if let list = node as? ListValue { + case .listValue: + let list = node as! ListValue guard let type = getNullableType(type: context.parentInputType) else { return .continue } @@ -29,8 +30,8 @@ func ValuesOfCorrectTypeRule(context: ValidationContext) -> Visitor { return .break // Don't traverse further. } return .continue - } - if let object = node as? ObjectValue { + case .objectValue: + let object = node as! ObjectValue let type = getNamedType(type: context.inputType) guard let type = type as? GraphQLInputObjectType else { isValidValueNode(context, object) @@ -64,8 +65,8 @@ func ValuesOfCorrectTypeRule(context: ValidationContext) -> Visitor { ) } return .continue - } - if let field = node as? ObjectField { + case .objectField: + let field = node as! ObjectField let parentType = getNamedType(type: context.parentInputType) if context.inputType == nil, @@ -86,8 +87,8 @@ func ValuesOfCorrectTypeRule(context: ValidationContext) -> Visitor { ) } return .continue - } - if let null = node as? NullValue { + case .nullValue: + let null = node as! NullValue let type = context.inputType if let type = type as? GraphQLNonNull { context.report( @@ -99,28 +100,13 @@ func ValuesOfCorrectTypeRule(context: ValidationContext) -> Visitor { ) } return .continue - } - if let node = node as? EnumValue { - isValidValueNode(context, node) - return .continue - } - if let node = node as? IntValue { + case .enumValue, .intValue, .floatValue, .stringValue, .booleanValue: + let node = node as! Value isValidValueNode(context, node) return .continue - } - if let node = node as? FloatValue { - isValidValueNode(context, node) - return .continue - } - if let node = node as? StringValue { - isValidValueNode(context, node) - return .continue - } - if let node = node as? BooleanValue { - isValidValueNode(context, node) + default: return .continue } - return .continue } ) } diff --git a/Sources/GraphQL/Validation/Rules/VariablesAreInputTypesRule.swift b/Sources/GraphQL/Validation/Rules/VariablesAreInputTypesRule.swift index 847d576a..60f5ef7c 100644 --- a/Sources/GraphQL/Validation/Rules/VariablesAreInputTypesRule.swift +++ b/Sources/GraphQL/Validation/Rules/VariablesAreInputTypesRule.swift @@ -10,7 +10,9 @@ func VariablesAreInputTypesRule(context: ValidationContext) -> Visitor { return Visitor( enter: { node, _, _, _, _ in - if let variableDefinition = node as? VariableDefinition { + switch node.kind { + case .variableDefinition: + let variableDefinition = node as! VariableDefinition let variableType = variableDefinition.type if let type = typeFromAST(schema: context.schema, inputTypeAST: variableType) { guard !isInputType(type: type) else { @@ -26,8 +28,10 @@ func VariablesAreInputTypesRule(context: ValidationContext) -> Visitor { ) ) } + return .continue + default: + return .continue } - return .continue } ) } diff --git a/Sources/GraphQL/Validation/ValidationContext.swift b/Sources/GraphQL/Validation/ValidationContext.swift index 708835e4..a206f7bc 100644 --- a/Sources/GraphQL/Validation/ValidationContext.swift +++ b/Sources/GraphQL/Validation/ValidationContext.swift @@ -204,19 +204,22 @@ public final class ValidationContext: ASTValidationContext { visitor: visitWithTypeInfo( typeInfo: typeInfo, visitor: Visitor(enter: { node, _, _, _, _ in - if node is VariableDefinition { + switch node.kind { + case .variableDefinition: return .skip + case .variable: + let variable = node as! Variable + usages.append( + VariableUsage( + node: variable, + type: typeInfo.inputType, + defaultValue: typeInfo.defaultValue + ) + ) + return .continue + default: + return .continue } - - if let variable = node as? Variable { - usages.append(VariableUsage( - node: variable, - type: typeInfo.inputType, - defaultValue: typeInfo.defaultValue - )) - } - - return .continue }) ) ) From 2545f2a4eb759536bcde55ed45a90b3ed0eab2f9 Mon Sep 17 00:00:00 2001 From: Jay Herron Date: Mon, 22 Dec 2025 15:36:19 -0700 Subject: [PATCH 4/6] refactor(perf): Presizes GraphQLFieldDefinitionMap This improves benchmarked `graphql` time by 5% --- Sources/GraphQL/Type/Definition.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/GraphQL/Type/Definition.swift b/Sources/GraphQL/Type/Definition.swift index 20b15488..52540535 100644 --- a/Sources/GraphQL/Type/Definition.swift +++ b/Sources/GraphQL/Type/Definition.swift @@ -351,7 +351,7 @@ extension GraphQLObjectType: Hashable { } func defineFieldMap(name: String, fields: GraphQLFieldMap) throws -> GraphQLFieldDefinitionMap { - var fieldMap = GraphQLFieldDefinitionMap() + var fieldMap = GraphQLFieldDefinitionMap(minimumCapacity: fields.count) for (name, config) in fields { try assertValid(name: name) From 771524980e535f65c2788463506291b339caab49 Mon Sep 17 00:00:00 2001 From: Jay Herron Date: Mon, 22 Dec 2025 15:56:54 -0700 Subject: [PATCH 5/6] refactor(perf): Adds `fieldCache` and `interfaceCache` Improves benchmarked `graphql` time by 22% --- Sources/GraphQL/Type/Definition.swift | 118 +++++++++++++++++++++----- 1 file changed, 96 insertions(+), 22 deletions(-) diff --git a/Sources/GraphQL/Type/Definition.swift b/Sources/GraphQL/Type/Definition.swift index 52540535..2b125ae5 100644 --- a/Sources/GraphQL/Type/Definition.swift +++ b/Sources/GraphQL/Type/Definition.swift @@ -275,10 +275,37 @@ extension GraphQLScalarType: Hashable { public final class GraphQLObjectType: @unchecked Sendable { public let name: String public let description: String? + // While technically not sendable, fields and interfaces should not be mutated after schema // creation. - public var fields: () throws -> GraphQLFieldMap - public var interfaces: () throws -> [GraphQLInterfaceType] + public var fields: () throws -> GraphQLFieldMap { + get { + fieldFunc + } + set { + fieldFunc = newValue + // Clear the cache when setting a new function + fieldCache = nil + } + } + + private var fieldFunc: () throws -> GraphQLFieldMap + private var fieldCache: GraphQLFieldDefinitionMap? + + public var interfaces: () throws -> [GraphQLInterfaceType] { + get { + interfaceFunc + } + set { + interfaceFunc = newValue + // Clear the cache when setting a new function + interfaceCache = nil + } + } + + private var interfaceFunc: () throws -> [GraphQLInterfaceType] + private var interfaceCache: [GraphQLInterfaceType]? + public let isTypeOf: GraphQLIsTypeOf? public let astNode: ObjectTypeDefinition? public let extensionASTNodes: [TypeExtensionDefinition] @@ -296,8 +323,8 @@ public final class GraphQLObjectType: @unchecked Sendable { try assertValid(name: name) self.name = name self.description = description - self.fields = { fields } - self.interfaces = { interfaces } + fieldFunc = { fields } + interfaceFunc = { interfaces } self.isTypeOf = isTypeOf self.astNode = astNode self.extensionASTNodes = extensionASTNodes @@ -315,22 +342,32 @@ public final class GraphQLObjectType: @unchecked Sendable { try assertValid(name: name) self.name = name self.description = description - self.fields = fields - self.interfaces = interfaces + fieldFunc = fields + interfaceFunc = interfaces self.isTypeOf = isTypeOf self.astNode = astNode self.extensionASTNodes = extensionASTNodes } func getFields() throws -> GraphQLFieldDefinitionMap { - try defineFieldMap( - name: name, - fields: fields() - ) + // Cache on the first call + return try fieldCache ?? { + let fields = try defineFieldMap( + name: name, + fields: fields() + ) + self.fieldCache = fields + return fields + }() } func getInterfaces() throws -> [GraphQLInterfaceType] { - return try interfaces() + // Cache on the first call + return try interfaceCache ?? { + let interfaces = try interfaces() + self.interfaceCache = interfaces + return interfaces + }() } } @@ -657,10 +694,37 @@ public final class GraphQLInterfaceType: @unchecked Sendable { public let name: String public let description: String? public let resolveType: GraphQLTypeResolve? + // While technically not sendable, fields and interfaces should not be mutated after schema // creation. - public var fields: () throws -> GraphQLFieldMap - public var interfaces: () throws -> [GraphQLInterfaceType] + public var fields: () throws -> GraphQLFieldMap { + get { + fieldFunc + } + set { + fieldFunc = newValue + // Clear the cache when setting a new function + fieldCache = nil + } + } + + private var fieldFunc: () throws -> GraphQLFieldMap + private var fieldCache: GraphQLFieldDefinitionMap? + + public var interfaces: () throws -> [GraphQLInterfaceType] { + get { + interfaceFunc + } + set { + interfaceFunc = newValue + // Clear the cache when setting a new function + interfaceCache = nil + } + } + + private var interfaceFunc: () throws -> [GraphQLInterfaceType] + private var interfaceCache: [GraphQLInterfaceType]? + public let astNode: InterfaceTypeDefinition? public let extensionASTNodes: [InterfaceExtensionDefinition] public let kind: TypeKind = .interface @@ -677,8 +741,8 @@ public final class GraphQLInterfaceType: @unchecked Sendable { try assertValid(name: name) self.name = name self.description = description - self.fields = { fields } - self.interfaces = { interfaces } + fieldFunc = { fields } + interfaceFunc = { interfaces } self.resolveType = resolveType self.astNode = astNode self.extensionASTNodes = extensionASTNodes @@ -696,22 +760,32 @@ public final class GraphQLInterfaceType: @unchecked Sendable { try assertValid(name: name) self.name = name self.description = description - self.fields = fields - self.interfaces = interfaces + fieldFunc = fields + interfaceFunc = interfaces self.resolveType = resolveType self.astNode = astNode self.extensionASTNodes = extensionASTNodes } func getFields() throws -> GraphQLFieldDefinitionMap { - try defineFieldMap( - name: name, - fields: fields() - ) + // Cache on the first call + return try fieldCache ?? { + let fields = try defineFieldMap( + name: name, + fields: fields() + ) + self.fieldCache = fields + return fields + }() } func getInterfaces() throws -> [GraphQLInterfaceType] { - return try interfaces() + // Cache on the first call + return try interfaceCache ?? { + let interfaces = try interfaces() + self.interfaceCache = interfaces + return interfaces + }() } } From 0446903459275a710ebaa655d5b5014329099f5b Mon Sep 17 00:00:00 2001 From: Jay Herron Date: Tue, 23 Dec 2025 12:06:20 -0600 Subject: [PATCH 6/6] refactor(perf): Use `first` instead of `filter` Reduces benchmark `graphql` time by 4% --- Sources/GraphQL/Execution/Execute.swift | 5 +---- Sources/GraphQL/SwiftUtilities/Mirror.swift | 2 +- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/Sources/GraphQL/Execution/Execute.swift b/Sources/GraphQL/Execution/Execute.swift index b0a21f8f..e058ebcd 100644 --- a/Sources/GraphQL/Execution/Execute.swift +++ b/Sources/GraphQL/Execution/Execute.swift @@ -1057,10 +1057,7 @@ func defaultResolve( } let mirror = Mirror(reflecting: source) - guard let value = mirror.getValue(named: info.fieldName) else { - return nil - } - return value + return mirror.getValue(named: info.fieldName) } /** diff --git a/Sources/GraphQL/SwiftUtilities/Mirror.swift b/Sources/GraphQL/SwiftUtilities/Mirror.swift index c6c26b49..47cca3d4 100644 --- a/Sources/GraphQL/SwiftUtilities/Mirror.swift +++ b/Sources/GraphQL/SwiftUtilities/Mirror.swift @@ -16,7 +16,7 @@ func unwrap(_ value: any Sendable) -> (any Sendable)? { extension Mirror { func getValue(named key: String) -> (any Sendable)? { - guard let matched = children.filter({ $0.label == key }).first else { + guard let matched = children.first(where: { $0.label == key }) else { return nil }