UNPKG

40.5 kBPlain TextView Raw
1import path from "path";
2
3import {
4 GraphQLError,
5 GraphQLType,
6 getNamedType,
7 isCompositeType,
8 GraphQLEnumType,
9 GraphQLInputObjectType,
10 isNonNullType,
11 isListType,
12 isEnumType,
13 isInputObjectType,
14} from "graphql";
15
16import {
17 CompilerContext,
18 Operation,
19 Fragment,
20 SelectionSet,
21 Field,
22} from "apollo-codegen-core/lib/compiler";
23
24import {
25 SwiftGenerator,
26 Property,
27 Struct,
28 SwiftSource,
29 swift,
30} from "./language";
31import { Helpers } from "./helpers";
32import { isList } from "apollo-codegen-core/lib/utilities/graphql";
33
34import {
35 typeCaseForSelectionSet,
36 TypeCase,
37 Variant,
38} from "apollo-codegen-core/lib/compiler/visitors/typeCase";
39import { collectFragmentsReferenced } from "apollo-codegen-core/lib/compiler/visitors/collectFragmentsReferenced";
40import { generateOperationId } from "apollo-codegen-core/lib/compiler/visitors/generateOperationId";
41import { collectAndMergeFields } from "apollo-codegen-core/lib/compiler/visitors/collectAndMergeFields";
42
43import "apollo-codegen-core/lib/utilities/array";
44
45const { join, wrap } = SwiftSource;
46
47export interface Options {
48 namespace?: string;
49 passthroughCustomScalars?: boolean;
50 customScalarsPrefix?: string;
51}
52
53/**
54 * The main method to call from outside this package to generate Swift code.
55 *
56 * @param context The `CompilerContext` to use to generate code.
57 * @param outputIndividualFiles Generates individual files per query/fragment if true,
58 * otherwise shoves everything into one giant file.
59 * @param only [optional] The path to a file which is the only file which should be regenerated.
60 * If absent, all files will be regenerated.
61 */
62export function generateSource(
63 context: CompilerContext,
64 outputIndividualFiles: boolean,
65 suppressMultilineStringLiterals: boolean,
66 only?: string
67): SwiftAPIGenerator {
68 const generator = new SwiftAPIGenerator(context);
69
70 if (outputIndividualFiles) {
71 generator.withinFile(`Types.graphql.swift`, () => {
72 generator.fileHeader();
73
74 generator.namespaceDeclaration(context.options.namespace, () => {
75 context.typesUsed.forEach((type) => {
76 generator.typeDeclarationForGraphQLType(type, true);
77 });
78 });
79 });
80
81 const inputFilePaths = new Set<string>();
82
83 Object.values(context.operations).forEach((operation) => {
84 inputFilePaths.add(operation.filePath);
85 });
86
87 Object.values(context.fragments).forEach((fragment) => {
88 inputFilePaths.add(fragment.filePath);
89 });
90
91 for (const inputFilePath of inputFilePaths) {
92 if (only && inputFilePath !== only) continue;
93
94 generator.withinFile(`${path.basename(inputFilePath)}.swift`, () => {
95 generator.fileHeader();
96
97 generator.namespaceExtensionDeclaration(
98 context.options.namespace,
99 () => {
100 Object.values(context.operations).forEach((operation) => {
101 if (operation.filePath === inputFilePath) {
102 generator.classDeclarationForOperation(
103 operation,
104 true,
105 suppressMultilineStringLiterals
106 );
107 }
108 });
109
110 Object.values(context.fragments).forEach((fragment) => {
111 if (fragment.filePath === inputFilePath) {
112 generator.structDeclarationForFragment(
113 fragment,
114 true,
115 suppressMultilineStringLiterals
116 );
117 }
118 });
119 }
120 );
121 });
122 }
123 } else {
124 generator.fileHeader();
125
126 generator.namespaceDeclaration(context.options.namespace, () => {
127 context.typesUsed.forEach((type) => {
128 generator.typeDeclarationForGraphQLType(type, false);
129 });
130
131 Object.values(context.operations).forEach((operation) => {
132 generator.classDeclarationForOperation(
133 operation,
134 false,
135 suppressMultilineStringLiterals
136 );
137 });
138
139 Object.values(context.fragments).forEach((fragment) => {
140 generator.structDeclarationForFragment(
141 fragment,
142 false,
143 suppressMultilineStringLiterals
144 );
145 });
146 });
147 }
148
149 return generator;
150}
151
152export class SwiftAPIGenerator extends SwiftGenerator<CompilerContext> {
153 helpers: Helpers;
154
155 constructor(context: CompilerContext) {
156 super(context);
157
158 this.helpers = new Helpers(context.options);
159 }
160
161 fileHeader() {
162 this.printOnNewline(SwiftSource.raw`// @generated`);
163 this.printOnNewline(
164 SwiftSource.raw`// This file was automatically generated and should not be edited.`
165 );
166 this.printNewline();
167 this.printOnNewline(swift`import Apollo`);
168 this.printOnNewline(swift`import Foundation`);
169 }
170
171 /**
172 * Generates the class declaration for an operation.
173 *
174 * @param operation The operaton to generate the class declaration for.
175 * @param outputIndividualFiles If this operation is being output as individual files, to help prevent
176 * redundant usages of the `public` modifier in enum extensions.
177 */
178 classDeclarationForOperation(
179 operation: Operation,
180 outputIndividualFiles: boolean,
181 suppressMultilineStringLiterals: boolean
182 ) {
183 const { operationName, operationType, variables, source, selectionSet } =
184 operation;
185
186 let className;
187 let protocol;
188
189 switch (operationType) {
190 case "query":
191 className = `${this.helpers.operationClassName(operationName)}Query`;
192 protocol = "GraphQLQuery";
193 break;
194 case "mutation":
195 className = `${this.helpers.operationClassName(operationName)}Mutation`;
196 protocol = "GraphQLMutation";
197 break;
198 case "subscription":
199 className = `${this.helpers.operationClassName(
200 operationName
201 )}Subscription`;
202 protocol = "GraphQLSubscription";
203 break;
204 default:
205 throw new GraphQLError(`Unsupported operation type "${operationType}"`);
206 }
207
208 const {
209 options: { namespace },
210 fragments,
211 } = this.context;
212 const isRedundant = !!namespace && outputIndividualFiles;
213 const modifiers = isRedundant ? ["final"] : ["public", "final"];
214
215 this.classDeclaration(
216 {
217 className,
218 modifiers,
219 adoptedProtocols: [protocol],
220 },
221 () => {
222 if (source) {
223 this.comment("The raw GraphQL definition of this operation.");
224 this.printOnNewline(swift`public let operationDefinition: String =`);
225 this.withIndent(() => {
226 this.multilineString(source, suppressMultilineStringLiterals);
227 });
228 }
229
230 this.printNewlineIfNeeded();
231 this.printOnNewline(
232 swift`public let operationName: String = ${SwiftSource.string(
233 operationName
234 )}`
235 );
236
237 const fragmentsReferenced = collectFragmentsReferenced(
238 operation.selectionSet,
239 fragments
240 );
241
242 if (this.context.options.generateOperationIds) {
243 const { operationId, sourceWithFragments } = generateOperationId(
244 operation,
245 fragments,
246 fragmentsReferenced
247 );
248 operation.operationId = operationId;
249 operation.sourceWithFragments = sourceWithFragments;
250 this.printNewlineIfNeeded();
251 this.printOnNewline(
252 swift`public let operationIdentifier: String? = ${SwiftSource.string(
253 operationId
254 )}`
255 );
256 }
257
258 if (fragmentsReferenced.size > 0) {
259 this.printNewlineIfNeeded();
260 this.printOnNewline(swift`public var queryDocument: String`);
261 this.withinBlock(() => {
262 this.printOnNewline(
263 swift`var document: String = operationDefinition`
264 );
265 fragmentsReferenced.forEach((fragmentName) => {
266 this.printOnNewline(
267 swift`document.append("\\n" + ${this.helpers.structNameForFragmentName(
268 fragmentName
269 )}.fragmentDefinition)`
270 );
271 });
272 this.printOnNewline(swift`return document`);
273 });
274 }
275
276 this.printNewlineIfNeeded();
277
278 if (variables && variables.length > 0) {
279 const properties = variables.map(({ name, type }) => {
280 const typeName = this.helpers.typeNameFromGraphQLType(type);
281 const isOptional = !(
282 isNonNullType(type) ||
283 (isListType(type) && isNonNullType(type.ofType))
284 );
285 return { name, propertyName: name, type, typeName, isOptional };
286 });
287
288 this.propertyDeclarations(properties);
289
290 this.printNewlineIfNeeded();
291 this.initializerDeclarationForProperties(properties);
292
293 this.printNewlineIfNeeded();
294 this.printOnNewline(swift`public var variables: GraphQLMap?`);
295 this.withinBlock(() => {
296 this.printOnNewline(
297 wrap(
298 swift`return [`,
299 join(
300 properties.map(
301 ({ name, propertyName }) =>
302 swift`${SwiftSource.string(name)}: ${propertyName}`
303 ),
304 ", "
305 ) || swift`:`,
306 swift`]`
307 )
308 );
309 });
310 } else {
311 this.initializerDeclarationForProperties([]);
312 }
313
314 this.structDeclarationForSelectionSet(
315 {
316 structName: "Data",
317 selectionSet,
318 },
319 outputIndividualFiles
320 );
321 }
322 );
323 }
324
325 /**
326 * Generates the struct declaration for a fragment.
327 *
328 * @param param0 The fragment name, selectionSet, and source to use to generate the struct
329 * @param outputIndividualFiles If this operation is being output as individual files, to help prevent
330 * redundant usages of the `public` modifier in enum extensions.
331 */
332 structDeclarationForFragment(
333 { fragmentName, selectionSet, source }: Fragment,
334 outputIndividualFiles: boolean,
335 suppressMultilineStringLiterals: boolean
336 ) {
337 const structName = this.helpers.structNameForFragmentName(fragmentName);
338
339 this.structDeclarationForSelectionSet(
340 {
341 structName,
342 adoptedProtocols: ["GraphQLFragment"],
343 selectionSet,
344 },
345 outputIndividualFiles,
346 () => {
347 if (source) {
348 this.comment("The raw GraphQL definition of this fragment.");
349 this.printOnNewline(
350 swift`public static let fragmentDefinition: String =`
351 );
352 this.withIndent(() => {
353 this.multilineString(source, suppressMultilineStringLiterals);
354 });
355 }
356 }
357 );
358 }
359
360 /**
361 * Generates the struct declaration for a selection set.
362 *
363 * @param param0 The name, adoptedProtocols, and selectionSet to use to generate the struct
364 * @param outputIndividualFiles If this operation is being output as individual files, to help prevent
365 * redundant usages of the `public` modifier in enum extensions.
366 * @param before [optional] A function to execute before generating the struct declaration.
367 */
368 structDeclarationForSelectionSet(
369 {
370 structName,
371 adoptedProtocols = ["GraphQLSelectionSet"],
372 selectionSet,
373 }: {
374 structName: string;
375 adoptedProtocols?: string[];
376 selectionSet: SelectionSet;
377 },
378 outputIndividualFiles: boolean,
379 before?: Function
380 ) {
381 const typeCase = typeCaseForSelectionSet(
382 selectionSet,
383 !!this.context.options.mergeInFieldsFromFragmentSpreads
384 );
385
386 this.structDeclarationForVariant(
387 {
388 structName,
389 adoptedProtocols,
390 variant: typeCase.default,
391 typeCase,
392 },
393 outputIndividualFiles,
394 before,
395 () => {
396 const variants = typeCase.variants.map(
397 this.helpers.propertyFromVariant,
398 this.helpers
399 );
400
401 for (const variant of variants) {
402 this.propertyDeclarationForVariant(variant);
403
404 this.structDeclarationForVariant(
405 {
406 structName: variant.structName,
407 variant,
408 },
409 outputIndividualFiles
410 );
411 }
412 }
413 );
414 }
415
416 /**
417 * Generates the struct declaration for a variant
418 *
419 * @param param0 The structName, adoptedProtocols, variant, and typeCase to use to generate the struct
420 * @param outputIndividualFiles If this operation is being output as individual files, to help prevent
421 * redundant usages of the `public` modifier in enum extensions.
422 * @param before [optional] A function to execute before generating the struct declaration.
423 * @param after [optional] A function to execute after generating the struct declaration.
424 */
425 structDeclarationForVariant(
426 {
427 structName,
428 adoptedProtocols = ["GraphQLSelectionSet"],
429 variant,
430 typeCase,
431 }: {
432 structName: string;
433 adoptedProtocols?: string[];
434 variant: Variant;
435 typeCase?: TypeCase;
436 },
437 outputIndividualFiles: boolean,
438 before?: Function,
439 after?: Function
440 ) {
441 const {
442 options: {
443 namespace,
444 mergeInFieldsFromFragmentSpreads,
445 omitDeprecatedEnumCases,
446 },
447 } = this.context;
448
449 this.structDeclaration(
450 { structName, adoptedProtocols, namespace },
451 outputIndividualFiles,
452 () => {
453 if (before) {
454 before();
455 }
456
457 this.printNewlineIfNeeded();
458 this.printOnNewline(
459 swift`public static let possibleTypes: [String] = [`
460 );
461 this.print(
462 join(
463 variant.possibleTypes.map(
464 (type) => swift`${SwiftSource.string(type.name)}`
465 ),
466 ", "
467 )
468 );
469 this.print(swift`]`);
470
471 this.printNewlineIfNeeded();
472 this.printOnNewline(
473 swift`public static var selections: [GraphQLSelection] {`
474 );
475 this.withIndent(() => {
476 this.printOnNewline(swift`return `);
477 if (typeCase) {
478 this.typeCaseInitialization(typeCase);
479 } else {
480 this.selectionSetInitialization(variant);
481 }
482 });
483 this.printOnNewline(swift`}`);
484 this.printNewlineIfNeeded();
485
486 this.printOnNewline(
487 swift`public private(set) var resultMap: ResultMap`
488 );
489
490 this.printNewlineIfNeeded();
491 this.printOnNewline(swift`public init(unsafeResultMap: ResultMap)`);
492 this.withinBlock(() => {
493 this.printOnNewline(swift`self.resultMap = unsafeResultMap`);
494 });
495
496 if (typeCase) {
497 this.initializersForTypeCase(typeCase);
498 } else {
499 this.initializersForVariant(variant);
500 }
501
502 const fields = collectAndMergeFields(
503 variant,
504 !!mergeInFieldsFromFragmentSpreads
505 ).map((field) => this.helpers.propertyFromField(field as Field));
506
507 const fragmentSpreads = variant.fragmentSpreads.map(
508 (fragmentSpread) => {
509 const isConditional = variant.possibleTypes.some(
510 (type) =>
511 !fragmentSpread.selectionSet.possibleTypes.includes(type)
512 );
513
514 return this.helpers.propertyFromFragmentSpread(
515 fragmentSpread,
516 isConditional
517 );
518 }
519 );
520
521 fields.forEach(this.propertyDeclarationForField, this);
522
523 if (fragmentSpreads.length > 0) {
524 this.printNewlineIfNeeded();
525 this.printOnNewline(swift`public var fragments: Fragments`);
526 this.withinBlock(() => {
527 this.printOnNewline(swift`get`);
528 this.withinBlock(() => {
529 this.printOnNewline(
530 swift`return Fragments(unsafeResultMap: resultMap)`
531 );
532 });
533 this.printOnNewline(swift`set`);
534 this.withinBlock(() => {
535 this.printOnNewline(swift`resultMap += newValue.resultMap`);
536 });
537 });
538
539 this.structDeclaration(
540 {
541 structName: "Fragments",
542 },
543 outputIndividualFiles,
544 () => {
545 this.printOnNewline(
546 swift`public private(set) var resultMap: ResultMap`
547 );
548
549 this.printNewlineIfNeeded();
550 this.printOnNewline(
551 swift`public init(unsafeResultMap: ResultMap)`
552 );
553 this.withinBlock(() => {
554 this.printOnNewline(swift`self.resultMap = unsafeResultMap`);
555 });
556
557 for (const fragmentSpread of fragmentSpreads) {
558 const { propertyName, typeName, structName, isConditional } =
559 fragmentSpread;
560
561 this.printNewlineIfNeeded();
562 this.printOnNewline(
563 swift`public var ${propertyName}: ${typeName}`
564 );
565 this.withinBlock(() => {
566 this.printOnNewline(swift`get`);
567 this.withinBlock(() => {
568 if (isConditional) {
569 this.printOnNewline(
570 swift`if !${structName}.possibleTypes.contains(resultMap["__typename"]! as! String) { return nil }`
571 );
572 }
573 this.printOnNewline(
574 swift`return ${structName}(unsafeResultMap: resultMap)`
575 );
576 });
577 this.printOnNewline(swift`set`);
578 this.withinBlock(() => {
579 if (isConditional) {
580 this.printOnNewline(
581 swift`guard let newValue = newValue else { return }`
582 );
583 this.printOnNewline(
584 swift`resultMap += newValue.resultMap`
585 );
586 } else {
587 this.printOnNewline(
588 swift`resultMap += newValue.resultMap`
589 );
590 }
591 });
592 });
593 }
594 }
595 );
596 }
597
598 for (const field of fields) {
599 if (isCompositeType(getNamedType(field.type)) && field.selectionSet) {
600 this.structDeclarationForSelectionSet(
601 {
602 structName: field.structName,
603 selectionSet: field.selectionSet,
604 },
605 outputIndividualFiles
606 );
607 }
608 }
609
610 if (after) {
611 after();
612 }
613 }
614 );
615 }
616
617 initializersForTypeCase(typeCase: TypeCase) {
618 const variants = typeCase.variants;
619
620 if (variants.length == 0) {
621 this.initializersForVariant(typeCase.default);
622 } else {
623 const remainder = typeCase.remainder;
624 for (const variant of remainder ? [remainder, ...variants] : variants) {
625 this.initializersForVariant(
626 variant,
627 variant === remainder
628 ? undefined
629 : this.helpers.structNameForVariant(variant),
630 false
631 );
632 }
633 }
634 }
635
636 initializersForVariant(
637 variant: Variant,
638 namespace?: string,
639 useInitializerIfPossible: boolean = true
640 ) {
641 if (useInitializerIfPossible && variant.possibleTypes.length == 1) {
642 const properties = this.helpers.propertiesForSelectionSet(variant);
643 if (!properties) return;
644
645 this.printNewlineIfNeeded();
646 this.printOnNewline(swift`public init`);
647
648 this.parametersForProperties(properties);
649
650 this.withinBlock(() => {
651 this.printOnNewline(
652 wrap(
653 swift`self.init(unsafeResultMap: [`,
654 join(
655 [
656 swift`"__typename": ${SwiftSource.string(
657 variant.possibleTypes[0].toString()
658 )}`,
659 ...properties.map((p) =>
660 this.propertyAssignmentForField(p, properties)
661 ),
662 ],
663 ", "
664 ) || swift`:`,
665 swift`])`
666 )
667 );
668 });
669 } else {
670 const structName = this.scope.typeName;
671
672 for (const possibleType of variant.possibleTypes) {
673 const properties = this.helpers.propertiesForSelectionSet(
674 {
675 possibleTypes: [possibleType],
676 selections: variant.selections,
677 },
678 namespace
679 );
680
681 if (!properties) continue;
682
683 this.printNewlineIfNeeded();
684 this.printOnNewline(
685 SwiftSource.raw`public static func make${possibleType}`
686 );
687
688 this.parametersForProperties(properties);
689
690 this.print(swift` -> ${structName}`);
691
692 this.withinBlock(() => {
693 this.printOnNewline(
694 wrap(
695 swift`return ${structName}(unsafeResultMap: [`,
696 join(
697 [
698 swift`"__typename": ${SwiftSource.string(
699 possibleType.toString()
700 )}`,
701 ...properties.map((p) =>
702 this.propertyAssignmentForField(p, properties)
703 ),
704 ],
705 ", "
706 ) || swift`:`,
707 swift`])`
708 )
709 );
710 });
711 }
712 }
713 }
714
715 propertyAssignmentForField(
716 field: {
717 responseKey: string;
718 propertyName: string;
719 type: GraphQLType;
720 isConditional?: boolean;
721 structName?: string;
722 },
723 properties: { propertyName: string }[]
724 ): SwiftSource {
725 const { responseKey, propertyName, type, isConditional, structName } =
726 field;
727 const parameterName = this.helpers.internalParameterName(
728 propertyName,
729 properties
730 );
731 const valueExpression = isCompositeType(getNamedType(type))
732 ? this.helpers.mapExpressionForType(
733 type,
734 isConditional,
735 (expression) => swift`${expression}.resultMap`,
736 SwiftSource.identifier(parameterName),
737 structName!,
738 "ResultMap"
739 )
740 : SwiftSource.identifier(parameterName);
741 return swift`${SwiftSource.string(responseKey)}: ${valueExpression}`;
742 }
743
744 propertyDeclarationForField(field: Field & Property) {
745 const {
746 responseKey,
747 propertyName,
748 typeName,
749 type,
750 isOptional,
751 isConditional,
752 } = field;
753
754 const unmodifiedFieldType = getNamedType(type);
755
756 this.printNewlineIfNeeded();
757
758 this.comment(field.description);
759 this.deprecationAttributes(field.isDeprecated, field.deprecationReason);
760
761 this.printOnNewline(swift`public var ${propertyName}: ${typeName}`);
762 this.withinBlock(() => {
763 if (isCompositeType(unmodifiedFieldType)) {
764 const structName = this.helpers.structNameForPropertyName(responseKey);
765
766 if (isList(type)) {
767 this.printOnNewline(swift`get`);
768 this.withinBlock(() => {
769 const resultMapTypeName = this.helpers.typeNameFromGraphQLType(
770 type,
771 "ResultMap",
772 false
773 );
774 let expression;
775 if (isOptional) {
776 expression = swift`(resultMap[${SwiftSource.string(
777 responseKey
778 )}] as? ${resultMapTypeName})`;
779 } else {
780 expression = swift`(resultMap[${SwiftSource.string(
781 responseKey
782 )}] as! ${resultMapTypeName})`;
783 }
784 this.printOnNewline(
785 swift`return ${this.helpers.mapExpressionForType(
786 type,
787 isConditional,
788 (expression) =>
789 swift`${structName}(unsafeResultMap: ${expression})`,
790 expression,
791 "ResultMap",
792 structName
793 )}`
794 );
795 });
796 this.printOnNewline(swift`set`);
797 this.withinBlock(() => {
798 let newValueExpression = this.helpers.mapExpressionForType(
799 type,
800 isConditional,
801 (expression) => swift`${expression}.resultMap`,
802 swift`newValue`,
803 structName,
804 "ResultMap"
805 );
806 this.printOnNewline(
807 swift`resultMap.updateValue(${newValueExpression}, forKey: ${SwiftSource.string(
808 responseKey
809 )})`
810 );
811 });
812 } else {
813 this.printOnNewline(swift`get`);
814 this.withinBlock(() => {
815 if (isOptional) {
816 this.printOnNewline(
817 swift`return (resultMap[${SwiftSource.string(
818 responseKey
819 )}] as? ResultMap).flatMap { ${structName}(unsafeResultMap: $0) }`
820 );
821 } else {
822 this.printOnNewline(
823 swift`return ${structName}(unsafeResultMap: resultMap[${SwiftSource.string(
824 responseKey
825 )}]! as! ResultMap)`
826 );
827 }
828 });
829 this.printOnNewline(swift`set`);
830 this.withinBlock(() => {
831 let newValueExpression;
832 if (isOptional) {
833 newValueExpression = "newValue?.resultMap";
834 } else {
835 newValueExpression = "newValue.resultMap";
836 }
837 this.printOnNewline(
838 swift`resultMap.updateValue(${newValueExpression}, forKey: ${SwiftSource.string(
839 responseKey
840 )})`
841 );
842 });
843 }
844 } else {
845 this.printOnNewline(swift`get`);
846 this.withinBlock(() => {
847 if (isOptional) {
848 this.printOnNewline(
849 swift`return resultMap[${SwiftSource.string(
850 responseKey
851 )}] as? ${typeName.slice(0, -1)}`
852 );
853 } else {
854 this.printOnNewline(
855 swift`return resultMap[${SwiftSource.string(
856 responseKey
857 )}]! as! ${typeName}`
858 );
859 }
860 });
861 this.printOnNewline(swift`set`);
862 this.withinBlock(() => {
863 this.printOnNewline(
864 swift`resultMap.updateValue(newValue, forKey: ${SwiftSource.string(
865 responseKey
866 )})`
867 );
868 });
869 }
870 });
871 }
872
873 propertyDeclarationForVariant(variant: Property & Struct) {
874 const { propertyName, typeName, structName } = variant;
875
876 this.printNewlineIfNeeded();
877 this.printOnNewline(swift`public var ${propertyName}: ${typeName}`);
878 this.withinBlock(() => {
879 this.printOnNewline(swift`get`);
880 this.withinBlock(() => {
881 this.printOnNewline(
882 swift`if !${structName}.possibleTypes.contains(__typename) { return nil }`
883 );
884 this.printOnNewline(
885 swift`return ${structName}(unsafeResultMap: resultMap)`
886 );
887 });
888 this.printOnNewline(swift`set`);
889 this.withinBlock(() => {
890 this.printOnNewline(
891 swift`guard let newValue = newValue else { return }`
892 );
893 this.printOnNewline(swift`resultMap = newValue.resultMap`);
894 });
895 });
896 }
897
898 initializerDeclarationForProperties(properties: Property[]) {
899 this.printOnNewline(swift`public init`);
900 this.parametersForProperties(properties);
901
902 this.withinBlock(() => {
903 properties.forEach(({ propertyName }) => {
904 this.printOnNewline(
905 swift`self.${propertyName} = ${this.helpers.internalParameterName(
906 propertyName,
907 properties
908 )}`
909 );
910 });
911 });
912 }
913
914 parametersForProperties(properties: Property[]) {
915 this.print(swift`(`);
916 this.print(
917 join(
918 properties.map(({ propertyName, typeName, isOptional }) => {
919 const internalName = this.helpers.internalParameterName(
920 propertyName,
921 properties
922 );
923 const decl =
924 internalName === propertyName
925 ? propertyName
926 : swift`${propertyName} ${internalName}`;
927 return join([
928 swift`${decl}: ${typeName}`,
929 isOptional ? swift` = nil` : undefined,
930 ]);
931 }),
932 ", "
933 )
934 );
935 this.print(swift`)`);
936 }
937
938 typeCaseInitialization(typeCase: TypeCase) {
939 if (typeCase.variants.length < 1) {
940 this.selectionSetInitialization(typeCase.default);
941 return;
942 }
943
944 this.print(swift`[`);
945 this.withIndent(() => {
946 this.printOnNewline(swift`GraphQLTypeCase(`);
947 this.withIndent(() => {
948 this.printOnNewline(swift`variants: [`);
949 this.print(
950 join(
951 typeCase.variants.flatMap((variant) => {
952 const structName = this.helpers.structNameForVariant(variant);
953 return variant.possibleTypes.map(
954 (type) =>
955 swift`${SwiftSource.string(
956 type.toString()
957 )}: ${structName}.selections`
958 );
959 }),
960 ", "
961 )
962 );
963 this.print(swift`],`);
964 this.printOnNewline(swift`default: `);
965 this.selectionSetInitialization(typeCase.default);
966 });
967 this.printOnNewline(swift`)`);
968 });
969 this.printOnNewline(swift`]`);
970 }
971
972 selectionSetInitialization(selectionSet: SelectionSet) {
973 this.print(swift`[`);
974 this.withIndent(() => {
975 for (const selection of selectionSet.selections) {
976 switch (selection.kind) {
977 case "Field": {
978 const { name, alias, args, type } = selection;
979 const responseKey = selection.alias || selection.name;
980 const structName =
981 this.helpers.structNameForPropertyName(responseKey);
982
983 this.printOnNewline(swift`GraphQLField(`);
984 this.print(
985 join(
986 [
987 swift`${SwiftSource.string(name)}`,
988 alias
989 ? swift`alias: ${SwiftSource.string(alias)}`
990 : undefined,
991 args && args.length
992 ? swift`arguments: ${this.helpers.dictionaryLiteralForFieldArguments(
993 args
994 )}`
995 : undefined,
996 swift`type: ${this.helpers.fieldTypeEnum(type, structName)}`,
997 ],
998 ", "
999 )
1000 );
1001 this.print(swift`),`);
1002 break;
1003 }
1004 case "BooleanCondition":
1005 this.printOnNewline(swift`GraphQLBooleanCondition(`);
1006 this.print(
1007 join(
1008 [
1009 swift`variableName: ${SwiftSource.string(
1010 selection.variableName
1011 )}`,
1012 swift`inverted: ${selection.inverted}`,
1013 swift`selections: `,
1014 ],
1015 ", "
1016 )
1017 );
1018 this.selectionSetInitialization(selection.selectionSet);
1019 this.print(swift`),`);
1020 break;
1021 case "TypeCondition": {
1022 this.printOnNewline(swift`GraphQLTypeCondition(`);
1023 this.print(
1024 join(
1025 [
1026 swift`possibleTypes: [${join(
1027 selection.selectionSet.possibleTypes.map(
1028 (type) => swift`${SwiftSource.string(type.name)}`
1029 ),
1030 ", "
1031 )}]`,
1032 swift`selections: `,
1033 ],
1034 ", "
1035 )
1036 );
1037 this.selectionSetInitialization(selection.selectionSet);
1038 this.print(swift`),`);
1039 break;
1040 }
1041 case "FragmentSpread": {
1042 const structName = this.helpers.structNameForFragmentName(
1043 selection.fragmentName
1044 );
1045 this.printOnNewline(
1046 swift`GraphQLFragmentSpread(${structName}.self),`
1047 );
1048 break;
1049 }
1050 }
1051 }
1052 });
1053 this.printOnNewline(swift`]`);
1054 }
1055
1056 /**
1057 * Generates a type declaration for the given `GraphQLType`
1058 *
1059 * @param type The graphQLType to generate a type declaration for.
1060 * @param outputIndividualFiles If this operation is being output as individual files, to help prevent
1061 * redundant usages of the `public` modifier in enum extensions.
1062 */
1063 typeDeclarationForGraphQLType(
1064 type: GraphQLType,
1065 outputIndividualFiles: boolean
1066 ) {
1067 if (isEnumType(type)) {
1068 this.enumerationDeclaration(type);
1069 } else if (isInputObjectType(type)) {
1070 this.structDeclarationForInputObjectType(type, outputIndividualFiles);
1071 }
1072 }
1073
1074 enumerationDeclaration(type: GraphQLEnumType) {
1075 const { name, description } = type;
1076 const values = type.getValues().filter((value) => {
1077 return (
1078 !value.isDeprecated || !this.context.options.omitDeprecatedEnumCases
1079 );
1080 });
1081
1082 this.printNewlineIfNeeded();
1083 this.comment(description || undefined);
1084 this.printOnNewline(
1085 swift`public enum ${name}: RawRepresentable, Equatable, Hashable, CaseIterable, Apollo.JSONDecodable, Apollo.JSONEncodable`
1086 );
1087 this.withinBlock(() => {
1088 this.printOnNewline(swift`public typealias RawValue = String`);
1089
1090 values.forEach((value) => {
1091 this.comment(value.description || undefined);
1092 this.deprecationAttributes(
1093 value.isDeprecated,
1094 value.deprecationReason || undefined
1095 );
1096 this.printOnNewline(
1097 swift`case ${this.helpers.enumCaseName(value.name)}`
1098 );
1099 });
1100 this.comment("Auto generated constant for unknown enum values");
1101 this.printOnNewline(swift`case __unknown(RawValue)`);
1102
1103 this.printNewlineIfNeeded();
1104 this.printOnNewline(swift`public init?(rawValue: RawValue)`);
1105 this.withinBlock(() => {
1106 this.printOnNewline(swift`switch rawValue`);
1107 this.withinBlock(() => {
1108 values.forEach((value) => {
1109 this.printOnNewline(
1110 swift`case ${SwiftSource.string(
1111 value.value
1112 )}: self = ${this.helpers.enumDotCaseName(value.name)}`
1113 );
1114 });
1115 this.printOnNewline(swift`default: self = .__unknown(rawValue)`);
1116 });
1117 });
1118
1119 this.printNewlineIfNeeded();
1120 this.printOnNewline(swift`public var rawValue: RawValue`);
1121 this.withinBlock(() => {
1122 this.printOnNewline(swift`switch self`);
1123 this.withinBlock(() => {
1124 values.forEach((value) => {
1125 this.printOnNewline(
1126 swift`case ${this.helpers.enumDotCaseName(
1127 value.name
1128 )}: return ${SwiftSource.string(value.value)}`
1129 );
1130 });
1131 this.printOnNewline(swift`case .__unknown(let value): return value`);
1132 });
1133 });
1134
1135 this.printNewlineIfNeeded();
1136 this.printOnNewline(
1137 swift`public static func == (lhs: ${name}, rhs: ${name}) -> Bool`
1138 );
1139 this.withinBlock(() => {
1140 this.printOnNewline(swift`switch (lhs, rhs)`);
1141 this.withinBlock(() => {
1142 values.forEach((value) => {
1143 const enumDotCaseName = this.helpers.enumDotCaseName(value.name);
1144 const tuple = swift`(${enumDotCaseName}, ${enumDotCaseName})`;
1145 this.printOnNewline(swift`case ${tuple}: return true`);
1146 });
1147 this.printOnNewline(
1148 swift`case (.__unknown(let lhsValue), .__unknown(let rhsValue)): return lhsValue == rhsValue`
1149 );
1150 this.printOnNewline(swift`default: return false`);
1151 });
1152 });
1153
1154 this.printNewlineIfNeeded();
1155 this.printOnNewline(swift`public static var allCases: [${name}]`);
1156 this.withinBlock(() => {
1157 this.printOnNewline(swift`return [`);
1158 values.forEach((value) => {
1159 const enumDotCaseName = this.helpers.enumDotCaseName(value.name);
1160 this.withIndent(() => {
1161 this.printOnNewline(swift`${enumDotCaseName},`);
1162 });
1163 });
1164 this.printOnNewline(swift`]`);
1165 });
1166 });
1167 }
1168
1169 /**
1170 * Generates a struct for a `GraphQLInputObjectType`.
1171 *
1172 * @param type The input type to generate code for
1173 * @param outputIndividualFiles If this operation is being output as individual files, to help prevent
1174 * redundant usages of the `public` modifier in enum extensions.
1175 */
1176 structDeclarationForInputObjectType(
1177 type: GraphQLInputObjectType,
1178 outputIndividualFiles: boolean
1179 ) {
1180 const { name: structName, description } = type;
1181 const adoptedProtocols = ["GraphQLMapConvertible"];
1182 const fields = Object.values(type.getFields());
1183
1184 const properties = fields.map(
1185 this.helpers.propertyFromInputField,
1186 this.helpers
1187 );
1188
1189 properties.forEach((property) => {
1190 if (property.isOptional) {
1191 property.typeName = `Swift.Optional<${property.typeName}>`;
1192 }
1193 });
1194
1195 this.structDeclaration(
1196 { structName, description: description || undefined, adoptedProtocols },
1197 outputIndividualFiles,
1198 () => {
1199 this.printOnNewline(swift`public var graphQLMap: GraphQLMap`);
1200
1201 this.printNewlineIfNeeded();
1202
1203 if (properties.length > 0) {
1204 this.comment("- Parameters:");
1205 properties.forEach((property) => {
1206 var propertyDescription = "";
1207 if (property.description) {
1208 propertyDescription = `: ${property.description}`;
1209 }
1210 this.comment(
1211 ` - ${property.propertyName}${propertyDescription}`,
1212 false
1213 );
1214 });
1215 }
1216
1217 this.printOnNewline(swift`public init`);
1218 this.print(swift`(`);
1219 this.print(
1220 join(
1221 properties.map(({ propertyName, typeName, isOptional }) => {
1222 const internalName = this.helpers.internalParameterName(
1223 propertyName,
1224 properties
1225 );
1226 const decl =
1227 internalName === propertyName
1228 ? propertyName
1229 : swift`${propertyName} ${internalName}`;
1230 return join([
1231 swift`${decl}: ${typeName}`,
1232 isOptional ? swift` = nil` : undefined,
1233 ]);
1234 }),
1235 ", "
1236 )
1237 );
1238 this.print(swift`)`);
1239
1240 this.withinBlock(() => {
1241 this.printOnNewline(
1242 wrap(
1243 swift`graphQLMap = [`,
1244 join(
1245 properties.map(
1246 ({ name, propertyName }) =>
1247 swift`${SwiftSource.string(
1248 name
1249 )}: ${this.helpers.internalParameterName(
1250 propertyName,
1251 properties
1252 )}`
1253 ),
1254 ", "
1255 ) || swift`:`,
1256 swift`]`
1257 )
1258 );
1259 });
1260
1261 for (const {
1262 name,
1263 propertyName,
1264 typeName,
1265 description,
1266 isOptional,
1267 } of properties) {
1268 this.printNewlineIfNeeded();
1269 this.comment(description || undefined);
1270 this.printOnNewline(swift`public var ${propertyName}: ${typeName}`);
1271 this.withinBlock(() => {
1272 this.printOnNewline(swift`get`);
1273 this.withinBlock(() => {
1274 if (isOptional) {
1275 this.printOnNewline(
1276 swift`return graphQLMap[${SwiftSource.string(
1277 name
1278 )}] as? ${typeName} ?? ${typeName}.none`
1279 );
1280 } else {
1281 this.printOnNewline(
1282 swift`return graphQLMap[${SwiftSource.string(
1283 name
1284 )}] as! ${typeName}`
1285 );
1286 }
1287 });
1288 this.printOnNewline(swift`set`);
1289 this.withinBlock(() => {
1290 this.printOnNewline(
1291 swift`graphQLMap.updateValue(newValue, forKey: ${SwiftSource.string(
1292 name
1293 )})`
1294 );
1295 });
1296 });
1297 }
1298 }
1299 );
1300 }
1301}
1302
\No newline at end of file