writeHostApi method

  1. @override
void writeHostApi(
  1. SwiftOptions generatorOptions,
  2. Root root,
  3. Indent indent,
  4. Api api, {
  5. required String dartPackageName,
})
override

Write the swift code that represents a host Api, api. Example: protocol Foo { Int32 add(x: Int32, y: Int32) }

Implementation

@override
void writeHostApi(
  SwiftOptions generatorOptions,
  Root root,
  Indent indent,
  Api api, {
  required String dartPackageName,
}) {
  assert(api.location == ApiLocation.host);

  final String apiName = api.name;

  final bool isCustomCodec = getCodecClasses(api, root).isNotEmpty;
  if (isCustomCodec) {
    _writeCodec(indent, api, root);
  }
  const List<String> generatedComments = <String>[
    ' Generated protocol from Pigeon that represents a handler of messages from Flutter.'
  ];
  addDocumentationComments(indent, api.documentationComments, _docCommentSpec,
      generatorComments: generatedComments);

  indent.write('protocol $apiName ');
  indent.addScoped('{', '}', () {
    for (final Method method in api.methods) {
      final _SwiftFunctionComponents components =
          _SwiftFunctionComponents.fromMethod(method);
      final List<String> argSignature =
          components.arguments.map((_SwiftFunctionArgument argument) {
        final String? label = argument.label;
        final String name = argument.name;
        final String type = _nullsafeSwiftTypeForDartType(argument.type);
        return '${label == null ? '' : '$label '}$name: $type';
      }).toList();

      final String returnType = method.returnType.isVoid
          ? 'Void'
          : _nullsafeSwiftTypeForDartType(method.returnType);

      final String escapeType =
          method.returnType.isVoid ? 'Void' : returnType;

      addDocumentationComments(
          indent, method.documentationComments, _docCommentSpec);

      if (method.isAsynchronous) {
        argSignature.add(
            'completion: @escaping (Result<$escapeType, Error>) -> Void');
        indent.writeln('func ${components.name}(${argSignature.join(', ')})');
      } else if (method.returnType.isVoid) {
        indent.writeln(
            'func ${components.name}(${argSignature.join(', ')}) throws');
      } else {
        indent.writeln(
            'func ${components.name}(${argSignature.join(', ')}) throws -> $returnType');
      }
    }
  });

  indent.newln();
  indent.writeln(
      '$_docCommentPrefix Generated setup class from Pigeon to handle messages through the `binaryMessenger`.');
  indent.write('class ${apiName}Setup ');
  indent.addScoped('{', '}', () {
    final String codecName = _getCodecName(api);
    indent.writeln('$_docCommentPrefix The codec used by $apiName.');
    String codecArgumentString = '';
    if (getCodecClasses(api, root).isNotEmpty) {
      codecArgumentString = ', codec: codec';
      indent.writeln(
          'static var codec: FlutterStandardMessageCodec { $codecName.shared }');
    }
    indent.writeln(
        '$_docCommentPrefix Sets up an instance of `$apiName` to handle messages through the `binaryMessenger`.');
    indent.write(
        'static func setUp(binaryMessenger: FlutterBinaryMessenger, api: $apiName?) ');
    indent.addScoped('{', '}', () {
      for (final Method method in api.methods) {
        final _SwiftFunctionComponents components =
            _SwiftFunctionComponents.fromMethod(method);

        final String channelName =
            makeChannelName(api, method, dartPackageName);
        final String varChannelName = '${method.name}Channel';
        addDocumentationComments(
            indent, method.documentationComments, _docCommentSpec);

        indent.writeln(
            'let $varChannelName = FlutterBasicMessageChannel(name: "$channelName", binaryMessenger: binaryMessenger$codecArgumentString)');
        indent.write('if let api = api ');
        indent.addScoped('{', '}', () {
          indent.write('$varChannelName.setMessageHandler ');
          final String messageVarName =
              method.parameters.isNotEmpty ? 'message' : '_';
          indent.addScoped('{ $messageVarName, reply in', '}', () {
            final List<String> methodArgument = <String>[];
            if (components.arguments.isNotEmpty) {
              indent.writeln('let args = message as! [Any?]');
              enumerate(components.arguments,
                  (int index, _SwiftFunctionArgument arg) {
                final String argName =
                    _getSafeArgumentName(index, arg.namedType);
                final String argIndex = 'args[$index]';
                final String fieldType = _swiftTypeForDartType(arg.type);

                _writeGenericCasting(
                    indent: indent,
                    value: argIndex,
                    variableName: argName,
                    fieldType: fieldType,
                    type: arg.type);

                if (arg.label == '_') {
                  methodArgument.add(argName);
                } else {
                  methodArgument.add('${arg.label ?? arg.name}: $argName');
                }
              });
            }
            final String tryStatement = method.isAsynchronous ? '' : 'try ';
            final String call =
                '${tryStatement}api.${components.name}(${methodArgument.join(', ')})';
            if (method.isAsynchronous) {
              final String resultName =
                  method.returnType.isVoid ? 'nil' : 'res';
              final String successVariableInit =
                  method.returnType.isVoid ? '' : '(let res)';
              indent.write('$call ');

              indent.addScoped('{ result in', '}', () {
                indent.write('switch result ');
                indent.addScoped('{', '}', () {
                  final String nullsafe =
                      method.returnType.isNullable ? '?' : '';
                  final String enumTag =
                      method.returnType.isEnum ? '$nullsafe.rawValue' : '';
                  indent.writeln('case .success$successVariableInit:');
                  indent.nest(1, () {
                    indent.writeln('reply(wrapResult($resultName$enumTag))');
                  });
                  indent.writeln('case .failure(let error):');
                  indent.nest(1, () {
                    indent.writeln('reply(wrapError(error))');
                  });
                });
              });
            } else {
              indent.write('do ');
              indent.addScoped('{', '}', () {
                if (method.returnType.isVoid) {
                  indent.writeln(call);
                  indent.writeln('reply(wrapResult(nil))');
                } else {
                  String enumTag = '';
                  if (method.returnType.isEnum) {
                    enumTag = '.rawValue';
                  }
                  enumTag =
                      method.returnType.isNullable && method.returnType.isEnum
                          ? '?$enumTag'
                          : enumTag;
                  indent.writeln('let result = $call');
                  indent.writeln('reply(wrapResult(result$enumTag))');
                }
              }, addTrailingNewline: false);
              indent.addScoped(' catch {', '}', () {
                indent.writeln('reply(wrapError(error))');
              });
            }
          });
        }, addTrailingNewline: false);
        indent.addScoped(' else {', '}', () {
          indent.writeln('$varChannelName.setMessageHandler(nil)');
        });
      }
    });
  });
}