Skip to content

Commit

Permalink
Generate local cache mutations (#2311)
Browse files Browse the repository at this point in the history
* Create base API for cache writes

* WIP: Writing test for cache mutations

* Make DataDict mutable

* WIP: Cache Mutation Tests

* Remove mock mutable selection set

* Add _modify to DataDict

* Fixed some more tests

* Re-add MockMutableRootSelectionSet as protocol instead of class

* JSONDecodingError Equatable

* Test deletion of referenced record throws error

* Add default __typename get/setter to MutableRootSelectionSet

* Test for adding new entity reference to cache mutation

* Finished ReadWriteFromStoreTests Refactor

* Fix StoreConcurrencyTests

* Fix WatchQueryTests

* Fix SQLIteCacheTests

* Add other operations and fragments to supported locations for cache mutation directive

* Add operationType to LocalCacheMutation

* Create LocalCacheMutationDefinitionTemplate and break out shared code from OperationDefinitionTemplate

* Make MutableSelectionSet conform to SelectionSet

* Generate SelectionSet Mutable TypeName

* Generate mutable field accessors

* WIP

* Generate Named Fragment getter and setter

* Finish generation of mutable selection sets

* Generate Mutable Fragments

* Compile directives on FragmentDefinition

* Improve __typename addition functionality in frontend

* Pretty print actual rendered on template test failure

* Strip local cache mutation directive from source definition

* Fix bug where __typename was added to inline fragments in Frontend

* Make Fragment definition generate selection set as mutable for local cache mutations

* Generate Local Cache Mutations into separate folder

* Add inclusion conditions to generated Fragment Accessors (#2312)

* Add resolution of inclusion conditions for conditional fragment conversion

* Generate inclusion conditions on Fragment Gettters

* Test

* Add Mutable Typealiases to generated Schema. Clean up

* Fixes for code review comments
  • Loading branch information
AnthonyMDev authored Jun 14, 2022
1 parent d545b86 commit 55623fe
Show file tree
Hide file tree
Showing 46 changed files with 1,773 additions and 190 deletions.
33 changes: 33 additions & 0 deletions Apollo.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,13 @@
DE6D07F927BC3B6D009F5F33 /* GraphQLInputField+Rendered.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE6D07F827BC3B6D009F5F33 /* GraphQLInputField+Rendered.swift */; };
DE6D07FD27BC3D53009F5F33 /* OperationDefinition_VariableDefinition_Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE6D07FA27BC3BE9009F5F33 /* OperationDefinition_VariableDefinition_Tests.swift */; };
DE6D07FF27BC7F78009F5F33 /* InputVariableRenderable.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE6D07FE27BC7F78009F5F33 /* InputVariableRenderable.swift */; };
DE71FDBC2853B67C005FA9CC /* LocalCacheMutationDefinitionTemplate.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE71FDBB2853B67C005FA9CC /* LocalCacheMutationDefinitionTemplate.swift */; };
DE71FDBE2853B6D3005FA9CC /* LocalCacheMutationDefinitionTemplateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE71FDBD2853B6D3005FA9CC /* LocalCacheMutationDefinitionTemplateTests.swift */; };
DE71FDC02853C242005FA9CC /* OperationTemplateRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE71FDBF2853C242005FA9CC /* OperationTemplateRenderer.swift */; };
DE71FDC22853C4C8005FA9CC /* MockLocalCacheMutation.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE71FDC12853C4C8005FA9CC /* MockLocalCacheMutation.swift */; };
DE71FDC42853D681005FA9CC /* SelectionSetTemplate_LocalCacheMutation_Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE71FDC32853D681005FA9CC /* SelectionSetTemplate_LocalCacheMutation_Tests.swift */; };
DE71FDC82857CF1E005FA9CC /* AllAnimalsLocalCacheMutation.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE71FDC62857CF1E005FA9CC /* AllAnimalsLocalCacheMutation.swift */; };
DE71FDC92857CF1E005FA9CC /* PetDetailsMutation.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE71FDC72857CF1E005FA9CC /* PetDetailsMutation.swift */; };
DE736F4626FA6EE6007187F2 /* InflectorKit in Frameworks */ = {isa = PBXBuildFile; productRef = E6E4209126A7DF4200B82624 /* InflectorKit */; };
DE796429276998B000978A03 /* IR+RootFieldBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE796428276998B000978A03 /* IR+RootFieldBuilder.swift */; };
DE79642B276999E700978A03 /* IRNamedFragmentBuilderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE79642A276999E700978A03 /* IRNamedFragmentBuilderTests.swift */; };
Expand Down Expand Up @@ -1141,7 +1147,13 @@
DE6D07F827BC3B6D009F5F33 /* GraphQLInputField+Rendered.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "GraphQLInputField+Rendered.swift"; sourceTree = "<group>"; };
DE6D07FA27BC3BE9009F5F33 /* OperationDefinition_VariableDefinition_Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OperationDefinition_VariableDefinition_Tests.swift; sourceTree = "<group>"; };
DE6D07FE27BC7F78009F5F33 /* InputVariableRenderable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InputVariableRenderable.swift; sourceTree = "<group>"; };
DE71FDBB2853B67C005FA9CC /* LocalCacheMutationDefinitionTemplate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalCacheMutationDefinitionTemplate.swift; sourceTree = "<group>"; };
DE71FDBD2853B6D3005FA9CC /* LocalCacheMutationDefinitionTemplateTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalCacheMutationDefinitionTemplateTests.swift; sourceTree = "<group>"; };
DE71FDBF2853C242005FA9CC /* OperationTemplateRenderer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OperationTemplateRenderer.swift; sourceTree = "<group>"; };
DE71FDC12853C4C8005FA9CC /* MockLocalCacheMutation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MockLocalCacheMutation.swift; sourceTree = "<group>"; };
DE71FDC32853D681005FA9CC /* SelectionSetTemplate_LocalCacheMutation_Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectionSetTemplate_LocalCacheMutation_Tests.swift; sourceTree = "<group>"; };
DE71FDC62857CF1E005FA9CC /* AllAnimalsLocalCacheMutation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AllAnimalsLocalCacheMutation.swift; sourceTree = "<group>"; };
DE71FDC72857CF1E005FA9CC /* PetDetailsMutation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PetDetailsMutation.swift; sourceTree = "<group>"; };
DE796428276998B000978A03 /* IR+RootFieldBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "IR+RootFieldBuilder.swift"; sourceTree = "<group>"; };
DE79642A276999E700978A03 /* IRNamedFragmentBuilderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IRNamedFragmentBuilderTests.swift; sourceTree = "<group>"; };
DE79642C27699A6A00978A03 /* IR+NamedFragmentBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "IR+NamedFragmentBuilder.swift"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -2282,6 +2294,7 @@
isa = PBXGroup;
children = (
DE296536279B3B8200BF9B49 /* SelectionSetTemplateTests.swift */,
DE71FDC32853D681005FA9CC /* SelectionSetTemplate_LocalCacheMutation_Tests.swift */,
DE296538279B3B8200BF9B49 /* SelectionSetTemplate_RenderOperation_Tests.swift */,
);
path = SelectionSet;
Expand Down Expand Up @@ -2350,6 +2363,7 @@
E6203343284F1D1100A291D1 /* MockUnionTemplate.swift */,
E64F7EC027A122300059C021 /* ObjectTemplate.swift */,
DE5FD5FC2769222D0033EE23 /* OperationDefinitionTemplate.swift */,
DE71FDBB2853B67C005FA9CC /* LocalCacheMutationDefinitionTemplate.swift */,
E6EE62F027DBE6F200627257 /* SchemaModuleNamespaceTemplate.swift */,
DE5FD60427694FA70033EE23 /* SchemaTemplate.swift */,
DE2739102769AEBA00B886EF /* SelectionSetTemplate.swift */,
Expand All @@ -2375,6 +2389,7 @@
DE6D07FA27BC3BE9009F5F33 /* OperationDefinition_VariableDefinition_Tests.swift */,
DE09F9C5270269F800795949 /* OperationDefinitionTemplate_DocumentType_Tests.swift */,
DE09066E27A4713F00211300 /* OperationDefinitionTemplateTests.swift */,
DE71FDBD2853B6D3005FA9CC /* LocalCacheMutationDefinitionTemplateTests.swift */,
E6EE62F227DBE75A00627257 /* SchemaModuleNamespaceTemplateTests.swift */,
DE5FD608276956C70033EE23 /* SchemaTemplateTests.swift */,
E6B42D0C27A4749100A3BD58 /* SwiftPackageManagerModuleTemplateTests.swift */,
Expand All @@ -2390,6 +2405,7 @@
isa = PBXGroup;
children = (
E66C014127FEB84F00FF5FA1 /* Operations */,
DE71FDC52857CF1E005FA9CC /* LocalCacheMutations */,
E66C015427FEB86800FF5FA1 /* Schema */,
96F32D3A27CCD16B00F3383C /* graphql */,
DE3C79A9260A6ACD00D2F4FF /* AnimalKingdomAPI.h */,
Expand Down Expand Up @@ -2479,10 +2495,21 @@
DE6D07F827BC3B6D009F5F33 /* GraphQLInputField+Rendered.swift */,
DE6D07FE27BC7F78009F5F33 /* InputVariableRenderable.swift */,
DE6B650B27C4293D00970E4E /* FieldArgumentRendering.swift */,
DE71FDBF2853C242005FA9CC /* OperationTemplateRenderer.swift */,
);
path = RenderingHelpers;
sourceTree = "<group>";
};
DE71FDC52857CF1E005FA9CC /* LocalCacheMutations */ = {
isa = PBXGroup;
children = (
DE71FDC62857CF1E005FA9CC /* AllAnimalsLocalCacheMutation.swift */,
DE71FDC72857CF1E005FA9CC /* PetDetailsMutation.swift */,
);
name = LocalCacheMutations;
path = AnimalKingdomAPI/Sources/LocalCacheMutations;
sourceTree = "<group>";
};
DE90FCD727FCC6D20084CC79 /* Operations */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -3843,6 +3870,7 @@
E610D8DB278EB0900023E495 /* InterfaceFileGenerator.swift in Sources */,
9B7B6F69233C2C0C00F32205 /* FileManager+Apollo.swift in Sources */,
E674DB41274C0A9B009BB90E /* Glob.swift in Sources */,
DE71FDC02853C242005FA9CC /* OperationTemplateRenderer.swift in Sources */,
E6203342284F1C9600A291D1 /* MockUnionFileGenerator.swift in Sources */,
DE6D07F927BC3B6D009F5F33 /* GraphQLInputField+Rendered.swift in Sources */,
9F1A966C258F34BB00A06EEB /* GraphQLSchema.swift in Sources */,
Expand All @@ -3859,6 +3887,7 @@
DE09114E27288B1F000648E5 /* SortedSelections.swift in Sources */,
DE5FD607276950CC0033EE23 /* IR+Schema.swift in Sources */,
E69BEDA52798B86D00000D10 /* InputObjectTemplate.swift in Sources */,
DE71FDBC2853B67C005FA9CC /* LocalCacheMutationDefinitionTemplate.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down Expand Up @@ -3942,9 +3971,11 @@
E61EF713275EC99A00191DA7 /* ApolloCodegenTests.swift in Sources */,
DE6D07FD27BC3D53009F5F33 /* OperationDefinition_VariableDefinition_Tests.swift in Sources */,
E6B4E9992798A8CB004EC8C4 /* InterfaceTemplateTests.swift in Sources */,
DE71FDBE2853B6D3005FA9CC /* LocalCacheMutationDefinitionTemplateTests.swift in Sources */,
E6EFDD0B27E8328E00B17FE5 /* TemplateRenderer_SchemaFile_Tests.swift in Sources */,
E6A6866627F63BDC008A1D13 /* FileGenerator_ResolvePath_Tests.swift in Sources */,
9F62DF8E2590539A00E6E808 /* SchemaIntrospectionTests.swift in Sources */,
DE71FDC42853D681005FA9CC /* SelectionSetTemplate_LocalCacheMutation_Tests.swift in Sources */,
E6D90D0D278FFE35009CAC5D /* SchemaFileGeneratorTests.swift in Sources */,
E6B42D0B27A4746800A3BD58 /* SchemaModuleFileGeneratorTests.swift in Sources */,
9B68F0552416B33300E97318 /* LineByLineComparison.swift in Sources */,
Expand Down Expand Up @@ -4245,6 +4276,7 @@
E66C015127FEB84F00FF5FA1 /* WarmBloodedDetails.swift in Sources */,
E66C014C27FEB84F00FF5FA1 /* PetDetails.swift in Sources */,
E66C016E27FEB86800FF5FA1 /* RelativeSize.swift in Sources */,
DE71FDC92857CF1E005FA9CC /* PetDetailsMutation.swift in Sources */,
DE223C24271F335D004A0148 /* Resources.swift in Sources */,
E66C017C27FEB86800FF5FA1 /* Pet.swift in Sources */,
E66C014D27FEB84F00FF5FA1 /* HeightInMeters.swift in Sources */,
Expand All @@ -4258,6 +4290,7 @@
E66C014F27FEB84F00FF5FA1 /* ClassroomPetsQuery.swift in Sources */,
E676333427FF857D00D8B953 /* HousePet.swift in Sources */,
E66C014E27FEB84F00FF5FA1 /* ClassroomPetDetails.swift in Sources */,
DE71FDC82857CF1E005FA9CC /* AllAnimalsLocalCacheMutation.swift in Sources */,
E66C016D27FEB86800FF5FA1 /* SkinCovering.swift in Sources */,
E66C017427FEB86800FF5FA1 /* Rat.swift in Sources */,
E66C017527FEB86800FF5FA1 /* Height.swift in Sources */,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// @generated
// This file was automatically generated and should not be edited.

import ApolloAPI
@_exported import enum ApolloAPI.GraphQLEnum
@_exported import enum ApolloAPI.GraphQLNullable

public class AllAnimalsLocalCacheMutation: GraphQLQuery {
public static let operationName: String = "AllAnimalsLocalCacheMutation"
public static let document: DocumentType = .notPersisted(
definition: .init(
"""
query AllAnimalsLocalCacheMutation {
allAnimals {
__typename
species
skinCovering
... on Bird {
wingspan
}
}
}
"""
))

public init() {}

public struct Data: AnimalKingdomAPI.SelectionSet {
public let data: DataDict
public init(data: DataDict) { self.data = data }

public static var __parentType: ParentType { .Object(AnimalKingdomAPI.Query.self) }
public static var selections: [Selection] { [
.field("allAnimals", [AllAnimal].self),
] }

public var allAnimals: [AllAnimal] { data["allAnimals"] }

/// AllAnimal
public struct AllAnimal: AnimalKingdomAPI.SelectionSet {
public let data: DataDict
public init(data: DataDict) { self.data = data }

public static var __parentType: ParentType { .Interface(AnimalKingdomAPI.Animal.self) }
public static var selections: [Selection] { [
.field("species", String.self),
.field("skinCovering", GraphQLEnum<SkinCovering>?.self),
.inlineFragment(AsBird.self),
] }

public var species: String { data["species"] }
public var skinCovering: GraphQLEnum<SkinCovering>? { data["skinCovering"] }

public var asBird: AsBird? { _asInlineFragment() }

/// AllAnimal.AsBird
public struct AsBird: AnimalKingdomAPI.InlineFragment {
public let data: DataDict
public init(data: DataDict) { self.data = data }

public static var __parentType: ParentType { .Object(AnimalKingdomAPI.Bird.self) }
public static var selections: [Selection] { [
.field("wingspan", Float.self),
] }

public var wingspan: Float { data["wingspan"] }
public var species: String { data["species"] }
public var skinCovering: GraphQLEnum<SkinCovering>? { data["skinCovering"] }
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// @generated
// This file was automatically generated and should not be edited.

import ApolloAPI
@_exported import enum ApolloAPI.GraphQLEnum
@_exported import enum ApolloAPI.GraphQLNullable

public struct PetDetailsMutation: AnimalKingdomAPI.MutableSelectionSet, Fragment {
public static var fragmentDefinition: StaticString { """
fragment PetDetailsMutation on Pet {
__typename
owner {
__typename
firstName
}
}
""" }

public var data: DataDict
public init(data: DataDict) { self.data = data }

public static var __parentType: ParentType { .Interface(AnimalKingdomAPI.Pet.self) }
public static var selections: [Selection] { [
.field("owner", Owner?.self),
] }

public var owner: Owner? {
get { data["owner"] }
set { data["owner"] = newValue }
}

/// Owner
public struct Owner: AnimalKingdomAPI.MutableSelectionSet {
public var data: DataDict
public init(data: DataDict) { self.data = data }

public static var __parentType: ParentType { .Object(AnimalKingdomAPI.Human.self) }
public static var selections: [Selection] { [
.field("firstName", String.self),
] }

public var firstName: String {
get { data["firstName"] }
set { data["firstName"] = newValue }
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ public class AllAnimalsIncludeSkipQuery: GraphQLQuery {
public let data: DataDict
public init(data: DataDict) { self.data = data }

public var heightInMeters: HeightInMeters? { _toFragment() }
public var heightInMeters: HeightInMeters? { _toFragment(if: !"skipHeightInMeters") }
}

/// AllAnimal.Height
Expand Down Expand Up @@ -289,7 +289,7 @@ public class AllAnimalsIncludeSkipQuery: GraphQLQuery {
public init(data: DataDict) { self.data = data }

public var petDetails: PetDetails { _toFragment() }
public var heightInMeters: HeightInMeters? { _toFragment() }
public var heightInMeters: HeightInMeters? { _toFragment(if: !"skipHeightInMeters") }
}

/// AllAnimal.AsPet.Height
Expand Down Expand Up @@ -421,7 +421,7 @@ public class AllAnimalsIncludeSkipQuery: GraphQLQuery {
public let data: DataDict
public init(data: DataDict) { self.data = data }

public var heightInMeters: HeightInMeters? { _toFragment() }
public var heightInMeters: HeightInMeters? { _toFragment(if: !"skipHeightInMeters") }
}

/// AllAnimal.AsClassroomPet.Height
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@ where Schema == AnimalKingdomAPI.Schema {}
public protocol InlineFragment: ApolloAPI.SelectionSet & ApolloAPI.InlineFragment
where Schema == AnimalKingdomAPI.Schema {}

public protocol MutableSelectionSet: ApolloAPI.MutableRootSelectionSet
where Schema == AnimalKingdomAPI.Schema {}

public protocol MutableInlineFragment: ApolloAPI.MutableSelectionSet & ApolloAPI.InlineFragment
where Schema == AnimalKingdomAPI.Schema {}

public enum Schema: SchemaConfiguration {
public static func objectType(forTypename __typename: String) -> Object.Type? {
switch __typename {
Expand All @@ -28,4 +34,4 @@ public enum Schema: SchemaConfiguration {
default: return nil
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
query AllAnimalsLocalCacheMutation @apollo_client_ios_localCacheMutation {
allAnimals {
species
skinCovering
... on Bird {
wingspan
}
}
}
5 changes: 5 additions & 0 deletions Sources/AnimalKingdomAPI/graphql/PetDetailsMutation.graphql
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
fragment PetDetailsMutation on Pet @apollo_client_ios_localCacheMutation {
owner {
firstName
}
}
30 changes: 28 additions & 2 deletions Sources/ApolloAPI/FragmentProtocols.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,42 @@ public protocol FragmentContainer {
init(data: DataDict)
}

public extension FragmentContainer {
extension FragmentContainer {

/// Converts a `SelectionSet` to a `Fragment` given a generic fragment type.
///
/// - Warning: This function is not supported for use outside of generated call sites.
/// Generated call sites are guaranteed by the GraphQL compiler to be safe.
/// Unsupported usage may result in unintended consequences including crashes.
func _toFragment<T: Fragment>() -> T {
@inlinable public func _toFragment<T: Fragment>() -> T {
_convertToFragment()
}

@usableFromInline func _convertToFragment<T: Fragment>()-> T {
return T.init(data: data)
}

@inlinable public func _toFragment<T: Fragment>(
if conditions: Selection.Conditions? = nil
) -> T? {
guard let conditions = conditions else {
return _convertToFragment()
}

return conditions.evaluate(with: data._variables) ? _convertToFragment() : nil
}

@inlinable public func _toFragment<T: Fragment>(
if conditions: [Selection.Condition]
) -> T? {
return _toFragment(if: Selection.Conditions([conditions]))
}

@inlinable public func _toFragment<T: Fragment>(
if condition: Selection.Condition
) -> T? {
return _toFragment(if: Selection.Conditions(condition))
}
}

/// A `FragmentContainer` to be used by `SelectionSet`s that have no fragments.
Expand Down
17 changes: 13 additions & 4 deletions Sources/ApolloAPI/LocalCacheMutation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,17 @@ public protocol LocalCacheMutation: AnyObject, Hashable {
}

public extension LocalCacheMutation {
static func ==(lhs: Self, rhs: Self) -> Bool {
lhs.variables?.jsonEncodableValue?.jsonValue == rhs.variables?.jsonEncodableValue?.jsonValue
var variables: GraphQLOperation.Variables? {
return nil
}

func hash(into hasher: inout Hasher) {
hasher.combine(variables?.jsonEncodableValue?.jsonValue)
}

static func ==(lhs: Self, rhs: Self) -> Bool {
lhs.variables?.jsonEncodableValue?.jsonValue == rhs.variables?.jsonEncodableValue?.jsonValue
}
}

public protocol MutableSelectionSet: SelectionSet {
Expand All @@ -32,9 +36,14 @@ public extension MutableSelectionSet {
public extension MutableSelectionSet where Fragments: FragmentContainer {
@inlinable var fragments: Fragments {
get { Self.Fragments(data: data) }
set { data._data = newValue.data._data}
_modify {
var f = Self.Fragments(data: data)
yield &f
self.data._data = f.data._data
}
@available(*, unavailable, message: "mutate properties of the fragment instead.")
set { preconditionFailure("") }
}
}

public protocol MutableRootSelectionSet: RootSelectionSet, MutableSelectionSet {}

Loading

0 comments on commit 55623fe

Please sign in to comment.