From 762d0d730354a5dadd877e2836bc5a11cf6eb6d7 Mon Sep 17 00:00:00 2001 From: Calvin Cestari <146856+calvincestari@users.noreply.github.com> Date: Fri, 9 May 2025 15:38:43 -0700 Subject: [PATCH 1/7] Refactor selection set initializer config check --- .../ApolloCodegenConfiguration.swift | 31 ++++++++++++------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/apollo-ios-codegen/Sources/ApolloCodegenLib/CodegenConfiguration/ApolloCodegenConfiguration.swift b/apollo-ios-codegen/Sources/ApolloCodegenLib/CodegenConfiguration/ApolloCodegenConfiguration.swift index 130489306..cd583a09a 100644 --- a/apollo-ios-codegen/Sources/ApolloCodegenLib/CodegenConfiguration/ApolloCodegenConfiguration.swift +++ b/apollo-ios-codegen/Sources/ApolloCodegenLib/CodegenConfiguration/ApolloCodegenConfiguration.swift @@ -1434,30 +1434,39 @@ extension ApolloCodegenConfiguration.OperationsFileOutput { extension ApolloCodegenConfiguration { /// Determine whether the operations files are output to the schema types module. func shouldGenerateSelectionSetInitializers(for operation: IR.Operation) -> Bool { - guard experimentalFeatures.fieldMerging == .all else { return false } - if operation.definition.isLocalCacheMutation { - return true + // test with merging off and local cache mutation + // look for field merging test that turns off and do the opposite - } else if options.selectionSetInitializers.contains(.operations) { + if operation.definition.isLocalCacheMutation { return true } else { - return options.selectionSetInitializers.contains(definitionNamed: operation.definition.name) + guard experimentalFeatures.fieldMerging == .all else { return false } + + if options.selectionSetInitializers.contains(.operations) { + return true + + } else { + return options.selectionSetInitializers.contains(definitionNamed: operation.definition.name) + } } } /// Determine whether the operations files are output to the schema types module. func shouldGenerateSelectionSetInitializers(for fragment: IR.NamedFragment) -> Bool { - guard experimentalFeatures.fieldMerging == .all else { return false } - - if options.selectionSetInitializers.contains(.namedFragments) { return true } - if fragment.definition.isLocalCacheMutation { return true - } - return options.selectionSetInitializers.contains(definitionNamed: fragment.definition.name) + } else { + guard experimentalFeatures.fieldMerging == .all else { return false } + + if options.selectionSetInitializers.contains(.namedFragments) { + return true + } else { + return options.selectionSetInitializers.contains(definitionNamed: fragment.definition.name) + } + } } } From a319423415a80106ccfbce15eacb06d45d8e8415 Mon Sep 17 00:00:00 2001 From: Calvin Cestari <146856+calvincestari@users.noreply.github.com> Date: Fri, 16 May 2025 14:21:21 -0700 Subject: [PATCH 2/7] Codegen configuration override --- .../ApolloCodegenLib/ApolloCodegen.swift | 42 ++++++++++++++++--- 1 file changed, 36 insertions(+), 6 deletions(-) diff --git a/apollo-ios-codegen/Sources/ApolloCodegenLib/ApolloCodegen.swift b/apollo-ios-codegen/Sources/ApolloCodegenLib/ApolloCodegen.swift index 542de62a2..a42cbadd0 100644 --- a/apollo-ios-codegen/Sources/ApolloCodegenLib/ApolloCodegen.swift +++ b/apollo-ios-codegen/Sources/ApolloCodegenLib/ApolloCodegen.swift @@ -299,34 +299,64 @@ public class ApolloCodegen { let mergeNamedFragmentFields = config.experimentalFeatures.fieldMerging.options .contains(.namedFragments) + lazy var cacheMutationContext: ConfigurationContext = { + ConfigurationContext( + config: ApolloCodegenConfiguration( + schemaNamespace: self.config.schemaNamespace, + input: self.config.input, + output: self.config.output, + options: self.config.options, + experimentalFeatures: ApolloCodegenConfiguration.ExperimentalFeatures( + fieldMerging: .all, + legacySafelistingCompatibleOperations: self.config.experimentalFeatures.legacySafelistingCompatibleOperations + ), + schemaDownload: self.config.schemaDownload, + operationManifest: self.config.operationManifest + ), + rootURL: self.config.rootURL + ) + }() + return try await nonFatalErrorCollectingTaskGroup() { group in for fragment in fragments { + let fragmentConfig = fragment.isLocalCacheMutation ? cacheMutationContext : self.config + group.addTask { let irFragment = await ir.build( fragment: fragment, - mergingNamedFragmentFields: mergeNamedFragmentFields + mergingNamedFragmentFields: fragment.isLocalCacheMutation ? true : mergeNamedFragmentFields ) - let errors = try await FragmentFileGenerator(irFragment: irFragment, config: self.config) - .generate(forConfig: self.config, fileManager: fileManager) + let errors = try await FragmentFileGenerator( + irFragment: irFragment, + config: fragmentConfig + ).generate( + forConfig: fragmentConfig, + fileManager: fileManager + ) return (irFragment.name, errors) } } for operation in operations { + let operationConfig = operation.isLocalCacheMutation ? cacheMutationContext : self.config + group.addTask { async let identifier = self.operationIdentifierFactory.identifier(for: operation) let irOperation = await ir.build( operation: operation, - mergingNamedFragmentFields: mergeNamedFragmentFields + mergingNamedFragmentFields: operation.isLocalCacheMutation ? true : mergeNamedFragmentFields ) let errors = try await OperationFileGenerator( irOperation: irOperation, operationIdentifier: await identifier, - config: self.config - ).generate(forConfig: self.config, fileManager: fileManager) + config: operationConfig + ).generate( + forConfig: operationConfig, + fileManager: fileManager + ) return (irOperation.name, errors) } } From dde842e22d05a802172c6d18105480eba340d358 Mon Sep 17 00:00:00 2001 From: Calvin Cestari <146856+calvincestari@users.noreply.github.com> Date: Fri, 16 May 2025 14:23:58 -0700 Subject: [PATCH 3/7] Add tests --- .../ApolloCodegenTests.swift | 472 ++++++++++++++++++ 1 file changed, 472 insertions(+) diff --git a/Tests/ApolloCodegenTests/ApolloCodegenTests.swift b/Tests/ApolloCodegenTests/ApolloCodegenTests.swift index a369c3c7c..12fc8bc48 100644 --- a/Tests/ApolloCodegenTests/ApolloCodegenTests.swift +++ b/Tests/ApolloCodegenTests/ApolloCodegenTests.swift @@ -2904,4 +2904,476 @@ class ApolloCodegenTests: XCTestCase { } + // MARK: - Local Cache Mutation + Field Merging Integration Tests + // + // These are integration tests because the codegen test wrapper infrastructure does not support overriding config + // values during the test. + + func test__fileRendering__givenLocalCacheMutationQuery_whenSelectionSetInitializersEmpty_andFileMergingNone_shouldGenerateFullSelectionSetInitializers() async throws { + // given + try createFile( + body: """ + type Query { + allAnimals: [Animal!] + } + + interface Animal { + species: String + predator: Animal + name: String + } + + interface Pet implements Animal { + species: String + predator: Animal + name: String + } + + type Dog implements Animal & Pet { + species: String + predator: Animal + name: String + } + """, + filename: "schema.graphqls" + ) + + try createFile( + body: """ + query TestOperation @apollo_client_ios_localCacheMutation { + allAnimals { + predator { + species + ... on Pet { + name + ...TestFragment + } + } + ... on Dog { + species + name + } + } + } + + fragment TestFragment on Animal { + predator { + species + } + } + """, + filename: "operation.graphql" + ) + + let fileManager = MockApolloFileManager(strict: false) + let expectation = expectation(description: "Received local cache mutation file data.") + + fileManager.mock(closure: .createFile({ path, data, attributes in + if path.hasSuffix("TestOperationLocalCacheMutation.graphql.swift") { + expect(data?.asString).to(equalLineByLine(""" + class TestOperationLocalCacheMutation: LocalCacheMutation { + static let operationType: GraphQLOperationType = .query + + public init() {} + + struct Data: TestSchema.MutableSelectionSet { + var __data: DataDict + init(_dataDict: DataDict) { __data = _dataDict } + + static var __parentType: any ApolloAPI.ParentType { TestSchema.Objects.Query } + static var __selections: [ApolloAPI.Selection] { [ + .field("allAnimals", [AllAnimal]?.self), + ] } + + var allAnimals: [AllAnimal]? { + get { __data["allAnimals"] } + set { __data["allAnimals"] = newValue } + } + + init( + allAnimals: [AllAnimal]? = nil + ) { + self.init(_dataDict: DataDict( + data: [ + "__typename": TestSchema.Objects.Query.typename, + "allAnimals": allAnimals._fieldData, + ], + fulfilledFragments: [ + ObjectIdentifier(TestOperationLocalCacheMutation.Data.self) + ] + )) + } + + /// AllAnimal + /// + /// Parent Type: `Animal` + struct AllAnimal: TestSchema.MutableSelectionSet { + var __data: DataDict + init(_dataDict: DataDict) { __data = _dataDict } + + static var __parentType: any ApolloAPI.ParentType { TestSchema.Interfaces.Animal } + static var __selections: [ApolloAPI.Selection] { [ + .field("__typename", String.self), + .field("predator", Predator?.self), + .inlineFragment(AsDog.self), + ] } + + var predator: Predator? { + get { __data["predator"] } + set { __data["predator"] = newValue } + } + + var asDog: AsDog? { _asInlineFragment() } + + init( + __typename: String, + predator: Predator? = nil + ) { + self.init(_dataDict: DataDict( + data: [ + "__typename": __typename, + "predator": predator._fieldData, + ], + fulfilledFragments: [ + ObjectIdentifier(TestOperationLocalCacheMutation.Data.AllAnimal.self) + ] + )) + } + + /// AllAnimal.Predator + /// + /// Parent Type: `Animal` + struct Predator: TestSchema.MutableSelectionSet { + var __data: DataDict + init(_dataDict: DataDict) { __data = _dataDict } + + static var __parentType: any ApolloAPI.ParentType { TestSchema.Interfaces.Animal } + static var __selections: [ApolloAPI.Selection] { [ + .field("__typename", String.self), + .field("species", String?.self), + .inlineFragment(AsPet.self), + ] } + + var species: String? { + get { __data["species"] } + set { __data["species"] = newValue } + } + + var asPet: AsPet? { _asInlineFragment() } + + init( + __typename: String, + species: String? = nil + ) { + self.init(_dataDict: DataDict( + data: [ + "__typename": __typename, + "species": species, + ], + fulfilledFragments: [ + ObjectIdentifier(TestOperationLocalCacheMutation.Data.AllAnimal.Predator.self) + ] + )) + } + + /// AllAnimal.Predator.AsPet + /// + /// Parent Type: `Pet` + struct AsPet: TestSchema.MutableInlineFragment { + var __data: DataDict + init(_dataDict: DataDict) { __data = _dataDict } + + typealias RootEntityType = TestOperationLocalCacheMutation.Data.AllAnimal.Predator + static var __parentType: any ApolloAPI.ParentType { TestSchema.Interfaces.Pet } + static var __selections: [ApolloAPI.Selection] { [ + .field("name", String?.self), + .fragment(TestFragment.self), + ] } + + var name: String? { + get { __data["name"] } + set { __data["name"] = newValue } + } + var species: String? { + get { __data["species"] } + set { __data["species"] = newValue } + } + var predator: Predator? { + get { __data["predator"] } + set { __data["predator"] = newValue } + } + + struct Fragments: FragmentContainer { + var __data: DataDict + init(_dataDict: DataDict) { __data = _dataDict } + + var testFragment: TestFragment { + get { _toFragment() } + _modify { var f = testFragment; yield &f; __data = f.__data } + } + } + + init( + __typename: String, + name: String? = nil, + species: String? = nil, + predator: Predator? = nil + ) { + self.init(_dataDict: DataDict( + data: [ + "__typename": __typename, + "name": name, + "species": species, + "predator": predator._fieldData, + ], + fulfilledFragments: [ + ObjectIdentifier(TestOperationLocalCacheMutation.Data.AllAnimal.Predator.self), + ObjectIdentifier(TestOperationLocalCacheMutation.Data.AllAnimal.Predator.AsPet.self), + ObjectIdentifier(TestFragment.self) + ] + )) + } + + typealias Predator = TestFragment.Predator + } + } + + /// AllAnimal.AsDog + /// + /// Parent Type: `Dog` + struct AsDog: TestSchema.MutableInlineFragment { + var __data: DataDict + init(_dataDict: DataDict) { __data = _dataDict } + + typealias RootEntityType = TestOperationLocalCacheMutation.Data.AllAnimal + static var __parentType: any ApolloAPI.ParentType { TestSchema.Objects.Dog } + static var __selections: [ApolloAPI.Selection] { [ + .field("species", String?.self), + .field("name", String?.self), + ] } + + var species: String? { + get { __data["species"] } + set { __data["species"] = newValue } + } + var name: String? { + get { __data["name"] } + set { __data["name"] = newValue } + } + var predator: Predator? { + get { __data["predator"] } + set { __data["predator"] = newValue } + } + + init( + species: String? = nil, + name: String? = nil, + predator: Predator? = nil + ) { + self.init(_dataDict: DataDict( + data: [ + "__typename": TestSchema.Objects.Dog.typename, + "species": species, + "name": name, + "predator": predator._fieldData, + ], + fulfilledFragments: [ + ObjectIdentifier(TestOperationLocalCacheMutation.Data.AllAnimal.self), + ObjectIdentifier(TestOperationLocalCacheMutation.Data.AllAnimal.AsDog.self) + ] + )) + } + } + } + } + } + + } + """, atLine: 7)) + + expectation.fulfill() + } + + return true + })) + + // when + let config = ApolloCodegen.ConfigurationContext( + config: ApolloCodegenConfiguration.mock( + input: .init( + schemaSearchPaths: [directoryURL.appendingPathComponent("schema.graphqls").path], + operationSearchPaths: [directoryURL.appendingPathComponent("operation.graphql").path] + ), + // Apollo codegen should override the next two value to force the generation of selection set initializers + // and perform all file merging for the local cache mutation. + options: .init(selectionSetInitializers: []), + experimentalFeatures: .init(fieldMerging: .none) + ), + rootURL: nil + ) + + let subject = ApolloCodegen( + config: config, + operationIdentifierFactory: OperationIdentifierFactory(), + itemsToGenerate: .code + ) + + let compilationResult = try await subject.compileGraphQLResult() + let ir = IRBuilder(compilationResult: compilationResult) + + try await subject.generateFiles( + compilationResult: compilationResult, + ir: ir, + fileManager: fileManager + ) + + // then + expect(fileManager.allClosuresCalled).to(beTrue()) + + await fulfillment(of: [expectation], timeout: 1) + } + + func test__fileRendering__givenLocalCacheMutationFragment_whenSelectionSetInitializersEmpty_andFileMergingNone_shouldGenerateFullSelectionSetInitializers() async throws { + // given + try createFile( + body: """ + type Query { + allAnimals: [Animal!] + } + + interface Animal { + species: String + predator: Animal + name: String + } + + interface Pet implements Animal { + species: String + predator: Animal + name: String + } + + type Dog implements Animal & Pet { + species: String + predator: Animal + name: String + } + """, + filename: "schema.graphqls" + ) + + try createFile( + body: """ + query TestOperation { + allAnimals { + predator { + ...PredatorFragment + } + ... on Dog { + ...PredatorFragment + } + } + } + + fragment PredatorFragment on Animal @apollo_client_ios_localCacheMutation { + species + name + } + """, + filename: "operation.graphql" + ) + + let fileManager = MockApolloFileManager(strict: false) + let expectation = expectation(description: "Received local cache mutation file data.") + + fileManager.mock(closure: .createFile({ path, data, attributes in + if path.hasSuffix("PredatorFragment.graphql.swift") { + expect(data?.asString).to(equalLineByLine(""" + struct PredatorFragment: TestSchema.MutableSelectionSet, Fragment { + static var fragmentDefinition: StaticString { + #"fragment PredatorFragment on Animal { __typename species name }"# + } + + var __data: DataDict + init(_dataDict: DataDict) { __data = _dataDict } + + static var __parentType: any ApolloAPI.ParentType { TestSchema.Interfaces.Animal } + static var __selections: [ApolloAPI.Selection] { [ + .field("__typename", String.self), + .field("species", String?.self), + .field("name", String?.self), + ] } + + var species: String? { + get { __data["species"] } + set { __data["species"] = newValue } + } + var name: String? { + get { __data["name"] } + set { __data["name"] = newValue } + } + + init( + __typename: String, + species: String? = nil, + name: String? = nil + ) { + self.init(_dataDict: DataDict( + data: [ + "__typename": __typename, + "species": species, + "name": name, + ], + fulfilledFragments: [ + ObjectIdentifier(PredatorFragment.self) + ] + )) + } + } + + } + """, atLine: 7)) + + expectation.fulfill() + } + + return true + })) + + // when + let config = ApolloCodegen.ConfigurationContext( + config: ApolloCodegenConfiguration.mock( + input: .init( + schemaSearchPaths: [directoryURL.appendingPathComponent("schema.graphqls").path], + operationSearchPaths: [directoryURL.appendingPathComponent("operation.graphql").path] + ), + // Apollo codegen should override the next two value to force the generation of selection set initializers + // and perform all file merging for the local cache mutation. + options: .init(selectionSetInitializers: []), + experimentalFeatures: .init(fieldMerging: .none) + ), + rootURL: nil + ) + + let subject = ApolloCodegen( + config: config, + operationIdentifierFactory: OperationIdentifierFactory(), + itemsToGenerate: .code + ) + + let compilationResult = try await subject.compileGraphQLResult() + let ir = IRBuilder(compilationResult: compilationResult) + + try await subject.generateFiles( + compilationResult: compilationResult, + ir: ir, + fileManager: fileManager + ) + + // then + expect(fileManager.allClosuresCalled).to(beTrue()) + + await fulfillment(of: [expectation], timeout: 1) + } + } From 00cca02848cb75b5580d5a07ea88729388fdf0f9 Mon Sep 17 00:00:00 2001 From: Calvin Cestari <146856+calvincestari@users.noreply.github.com> Date: Fri, 16 May 2025 14:31:20 -0700 Subject: [PATCH 4/7] Cleanup --- .../CodegenConfiguration/ApolloCodegenConfiguration.swift | 4 ---- 1 file changed, 4 deletions(-) diff --git a/apollo-ios-codegen/Sources/ApolloCodegenLib/CodegenConfiguration/ApolloCodegenConfiguration.swift b/apollo-ios-codegen/Sources/ApolloCodegenLib/CodegenConfiguration/ApolloCodegenConfiguration.swift index cd583a09a..b9cf9795b 100644 --- a/apollo-ios-codegen/Sources/ApolloCodegenLib/CodegenConfiguration/ApolloCodegenConfiguration.swift +++ b/apollo-ios-codegen/Sources/ApolloCodegenLib/CodegenConfiguration/ApolloCodegenConfiguration.swift @@ -1434,10 +1434,6 @@ extension ApolloCodegenConfiguration.OperationsFileOutput { extension ApolloCodegenConfiguration { /// Determine whether the operations files are output to the schema types module. func shouldGenerateSelectionSetInitializers(for operation: IR.Operation) -> Bool { - - // test with merging off and local cache mutation - // look for field merging test that turns off and do the opposite - if operation.definition.isLocalCacheMutation { return true From 1deec6f04a2b07b73f82feb4b532f4db3a38bed3 Mon Sep 17 00:00:00 2001 From: Calvin Cestari <146856+calvincestari@users.noreply.github.com> Date: Wed, 28 May 2025 15:55:55 -0700 Subject: [PATCH 5/7] Adds comment Co-authored-by: Anthony Miller --- .../Sources/ApolloCodegenLib/ApolloCodegen.swift | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/apollo-ios-codegen/Sources/ApolloCodegenLib/ApolloCodegen.swift b/apollo-ios-codegen/Sources/ApolloCodegenLib/ApolloCodegen.swift index a42cbadd0..cb391c252 100644 --- a/apollo-ios-codegen/Sources/ApolloCodegenLib/ApolloCodegen.swift +++ b/apollo-ios-codegen/Sources/ApolloCodegenLib/ApolloCodegen.swift @@ -299,7 +299,11 @@ public class ApolloCodegen { let mergeNamedFragmentFields = config.experimentalFeatures.fieldMerging.options .contains(.namedFragments) - lazy var cacheMutationContext: ConfigurationContext = { + /// A `ConfigurationContext` to use when generated local cache mutations. + /// + /// Local cache mutations require some codegen options to be overridden to generate valid objects. + /// This context overrides only the necessary properties, copying all other values from the user-provided `context`. + private lazy var cacheMutationContext: ConfigurationContext = { ConfigurationContext( config: ApolloCodegenConfiguration( schemaNamespace: self.config.schemaNamespace, From d5467ff5931accbc398b9e63ba78efb256338293 Mon Sep 17 00:00:00 2001 From: Calvin Cestari <146856+calvincestari@users.noreply.github.com> Date: Wed, 28 May 2025 16:41:45 -0700 Subject: [PATCH 6/7] Simplify tests --- .../ApolloCodegenTests.swift | 312 +----------------- 1 file changed, 4 insertions(+), 308 deletions(-) diff --git a/Tests/ApolloCodegenTests/ApolloCodegenTests.swift b/Tests/ApolloCodegenTests/ApolloCodegenTests.swift index 12fc8bc48..1855c2f48 100644 --- a/Tests/ApolloCodegenTests/ApolloCodegenTests.swift +++ b/Tests/ApolloCodegenTests/ApolloCodegenTests.swift @@ -2919,20 +2919,6 @@ class ApolloCodegenTests: XCTestCase { interface Animal { species: String - predator: Animal - name: String - } - - interface Pet implements Animal { - species: String - predator: Animal - name: String - } - - type Dog implements Animal & Pet { - species: String - predator: Animal - name: String } """, filename: "schema.graphqls" @@ -2942,22 +2928,6 @@ class ApolloCodegenTests: XCTestCase { body: """ query TestOperation @apollo_client_ios_localCacheMutation { allAnimals { - predator { - species - ... on Pet { - name - ...TestFragment - } - } - ... on Dog { - species - name - } - } - } - - fragment TestFragment on Animal { - predator { species } } @@ -2971,225 +2941,10 @@ class ApolloCodegenTests: XCTestCase { fileManager.mock(closure: .createFile({ path, data, attributes in if path.hasSuffix("TestOperationLocalCacheMutation.graphql.swift") { expect(data?.asString).to(equalLineByLine(""" - class TestOperationLocalCacheMutation: LocalCacheMutation { - static let operationType: GraphQLOperationType = .query - - public init() {} - - struct Data: TestSchema.MutableSelectionSet { - var __data: DataDict - init(_dataDict: DataDict) { __data = _dataDict } - - static var __parentType: any ApolloAPI.ParentType { TestSchema.Objects.Query } - static var __selections: [ApolloAPI.Selection] { [ - .field("allAnimals", [AllAnimal]?.self), - ] } - - var allAnimals: [AllAnimal]? { - get { __data["allAnimals"] } - set { __data["allAnimals"] = newValue } - } - init( allAnimals: [AllAnimal]? = nil ) { - self.init(_dataDict: DataDict( - data: [ - "__typename": TestSchema.Objects.Query.typename, - "allAnimals": allAnimals._fieldData, - ], - fulfilledFragments: [ - ObjectIdentifier(TestOperationLocalCacheMutation.Data.self) - ] - )) - } - - /// AllAnimal - /// - /// Parent Type: `Animal` - struct AllAnimal: TestSchema.MutableSelectionSet { - var __data: DataDict - init(_dataDict: DataDict) { __data = _dataDict } - - static var __parentType: any ApolloAPI.ParentType { TestSchema.Interfaces.Animal } - static var __selections: [ApolloAPI.Selection] { [ - .field("__typename", String.self), - .field("predator", Predator?.self), - .inlineFragment(AsDog.self), - ] } - - var predator: Predator? { - get { __data["predator"] } - set { __data["predator"] = newValue } - } - - var asDog: AsDog? { _asInlineFragment() } - - init( - __typename: String, - predator: Predator? = nil - ) { - self.init(_dataDict: DataDict( - data: [ - "__typename": __typename, - "predator": predator._fieldData, - ], - fulfilledFragments: [ - ObjectIdentifier(TestOperationLocalCacheMutation.Data.AllAnimal.self) - ] - )) - } - - /// AllAnimal.Predator - /// - /// Parent Type: `Animal` - struct Predator: TestSchema.MutableSelectionSet { - var __data: DataDict - init(_dataDict: DataDict) { __data = _dataDict } - - static var __parentType: any ApolloAPI.ParentType { TestSchema.Interfaces.Animal } - static var __selections: [ApolloAPI.Selection] { [ - .field("__typename", String.self), - .field("species", String?.self), - .inlineFragment(AsPet.self), - ] } - - var species: String? { - get { __data["species"] } - set { __data["species"] = newValue } - } - - var asPet: AsPet? { _asInlineFragment() } - - init( - __typename: String, - species: String? = nil - ) { - self.init(_dataDict: DataDict( - data: [ - "__typename": __typename, - "species": species, - ], - fulfilledFragments: [ - ObjectIdentifier(TestOperationLocalCacheMutation.Data.AllAnimal.Predator.self) - ] - )) - } - - /// AllAnimal.Predator.AsPet - /// - /// Parent Type: `Pet` - struct AsPet: TestSchema.MutableInlineFragment { - var __data: DataDict - init(_dataDict: DataDict) { __data = _dataDict } - - typealias RootEntityType = TestOperationLocalCacheMutation.Data.AllAnimal.Predator - static var __parentType: any ApolloAPI.ParentType { TestSchema.Interfaces.Pet } - static var __selections: [ApolloAPI.Selection] { [ - .field("name", String?.self), - .fragment(TestFragment.self), - ] } - - var name: String? { - get { __data["name"] } - set { __data["name"] = newValue } - } - var species: String? { - get { __data["species"] } - set { __data["species"] = newValue } - } - var predator: Predator? { - get { __data["predator"] } - set { __data["predator"] = newValue } - } - - struct Fragments: FragmentContainer { - var __data: DataDict - init(_dataDict: DataDict) { __data = _dataDict } - - var testFragment: TestFragment { - get { _toFragment() } - _modify { var f = testFragment; yield &f; __data = f.__data } - } - } - - init( - __typename: String, - name: String? = nil, - species: String? = nil, - predator: Predator? = nil - ) { - self.init(_dataDict: DataDict( - data: [ - "__typename": __typename, - "name": name, - "species": species, - "predator": predator._fieldData, - ], - fulfilledFragments: [ - ObjectIdentifier(TestOperationLocalCacheMutation.Data.AllAnimal.Predator.self), - ObjectIdentifier(TestOperationLocalCacheMutation.Data.AllAnimal.Predator.AsPet.self), - ObjectIdentifier(TestFragment.self) - ] - )) - } - - typealias Predator = TestFragment.Predator - } - } - - /// AllAnimal.AsDog - /// - /// Parent Type: `Dog` - struct AsDog: TestSchema.MutableInlineFragment { - var __data: DataDict - init(_dataDict: DataDict) { __data = _dataDict } - - typealias RootEntityType = TestOperationLocalCacheMutation.Data.AllAnimal - static var __parentType: any ApolloAPI.ParentType { TestSchema.Objects.Dog } - static var __selections: [ApolloAPI.Selection] { [ - .field("species", String?.self), - .field("name", String?.self), - ] } - - var species: String? { - get { __data["species"] } - set { __data["species"] = newValue } - } - var name: String? { - get { __data["name"] } - set { __data["name"] = newValue } - } - var predator: Predator? { - get { __data["predator"] } - set { __data["predator"] = newValue } - } - - init( - species: String? = nil, - name: String? = nil, - predator: Predator? = nil - ) { - self.init(_dataDict: DataDict( - data: [ - "__typename": TestSchema.Objects.Dog.typename, - "species": species, - "name": name, - "predator": predator._fieldData, - ], - fulfilledFragments: [ - ObjectIdentifier(TestOperationLocalCacheMutation.Data.AllAnimal.self), - ObjectIdentifier(TestOperationLocalCacheMutation.Data.AllAnimal.AsDog.self) - ] - )) - } - } - } - } - } - - } - """, atLine: 7)) + """, atLine: 26, ignoringExtraLines: true)) expectation.fulfill() } @@ -3243,20 +2998,6 @@ class ApolloCodegenTests: XCTestCase { interface Animal { species: String - predator: Animal - name: String - } - - interface Pet implements Animal { - species: String - predator: Animal - name: String - } - - type Dog implements Animal & Pet { - species: String - predator: Animal - name: String } """, filename: "schema.graphqls" @@ -3266,18 +3007,12 @@ class ApolloCodegenTests: XCTestCase { body: """ query TestOperation { allAnimals { - predator { - ...PredatorFragment - } - ... on Dog { - ...PredatorFragment - } + ...PredatorFragment } } fragment PredatorFragment on Animal @apollo_client_ios_localCacheMutation { species - name } """, filename: "operation.graphql" @@ -3289,50 +3024,11 @@ class ApolloCodegenTests: XCTestCase { fileManager.mock(closure: .createFile({ path, data, attributes in if path.hasSuffix("PredatorFragment.graphql.swift") { expect(data?.asString).to(equalLineByLine(""" - struct PredatorFragment: TestSchema.MutableSelectionSet, Fragment { - static var fragmentDefinition: StaticString { - #"fragment PredatorFragment on Animal { __typename species name }"# - } - - var __data: DataDict - init(_dataDict: DataDict) { __data = _dataDict } - - static var __parentType: any ApolloAPI.ParentType { TestSchema.Interfaces.Animal } - static var __selections: [ApolloAPI.Selection] { [ - .field("__typename", String.self), - .field("species", String?.self), - .field("name", String?.self), - ] } - - var species: String? { - get { __data["species"] } - set { __data["species"] = newValue } - } - var name: String? { - get { __data["name"] } - set { __data["name"] = newValue } - } - init( __typename: String, - species: String? = nil, - name: String? = nil + species: String? = nil ) { - self.init(_dataDict: DataDict( - data: [ - "__typename": __typename, - "species": species, - "name": name, - ], - fulfilledFragments: [ - ObjectIdentifier(PredatorFragment.self) - ] - )) - } - } - - } - """, atLine: 7)) + """, atLine: 26, ignoringExtraLines: true)) expectation.fulfill() } From 53512be4dba7f7baa0f8a6144fee5b2515d929f5 Mon Sep 17 00:00:00 2001 From: Calvin Cestari <146856+calvincestari@users.noreply.github.com> Date: Wed, 28 May 2025 16:51:59 -0700 Subject: [PATCH 7/7] Fix visibility accessor --- apollo-ios-codegen/Sources/ApolloCodegenLib/ApolloCodegen.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apollo-ios-codegen/Sources/ApolloCodegenLib/ApolloCodegen.swift b/apollo-ios-codegen/Sources/ApolloCodegenLib/ApolloCodegen.swift index cb391c252..f08338334 100644 --- a/apollo-ios-codegen/Sources/ApolloCodegenLib/ApolloCodegen.swift +++ b/apollo-ios-codegen/Sources/ApolloCodegenLib/ApolloCodegen.swift @@ -303,7 +303,7 @@ public class ApolloCodegen { /// /// Local cache mutations require some codegen options to be overridden to generate valid objects. /// This context overrides only the necessary properties, copying all other values from the user-provided `context`. - private lazy var cacheMutationContext: ConfigurationContext = { + lazy var cacheMutationContext: ConfigurationContext = { ConfigurationContext( config: ApolloCodegenConfiguration( schemaNamespace: self.config.schemaNamespace,